diff --git a/flutter/realm_flutter/demo/.flutter-plugins b/flutter/realm_flutter/demo/.flutter-plugins new file mode 100644 index 000000000..26787828a --- /dev/null +++ b/flutter/realm_flutter/demo/.flutter-plugins @@ -0,0 +1,2 @@ +# This is a generated file; do not edit or check into version control. +realm_flutter=/Users/macaka/lubo/flutter/realm-dart/flutter/realm_flutter/ diff --git a/flutter/realm_flutter/demo/.flutter-plugins-dependencies b/flutter/realm_flutter/demo/.flutter-plugins-dependencies new file mode 100644 index 000000000..ceb24b134 --- /dev/null +++ b/flutter/realm_flutter/demo/.flutter-plugins-dependencies @@ -0,0 +1 @@ +{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"realm_flutter","path":"/Users/macaka/lubo/flutter/realm-dart/flutter/realm_flutter/","dependencies":[]}],"android":[{"name":"realm_flutter","path":"/Users/macaka/lubo/flutter/realm-dart/flutter/realm_flutter/","dependencies":[]}],"macos":[],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"realm_flutter","dependencies":[]}],"date_created":"2020-11-03 23:30:34.749308","version":"1.22.2"} \ No newline at end of file diff --git a/flutter/realm_flutter/demo/README.md b/flutter/realm_flutter/demo/README.md new file mode 100755 index 000000000..f3bd78fa6 --- /dev/null +++ b/flutter/realm_flutter/demo/README.md @@ -0,0 +1,54 @@ +# provider_shopper + +A Flutter sample app that shows a state management approach using the [Provider][] package. +This is the app discussed in the [Simple app state management][simple] section of +[flutter.dev][]. + +![An animated gif of the app in action](https://camo.githubusercontent.com/cf301d68c65279a074aa3334ef7fff548f87c0e2/68747470733a2f2f666c75747465722e6465762f6173736574732f646576656c6f706d656e742f646174612d616e642d6261636b656e642f73746174652d6d676d742f6d6f64656c2d73686f707065722d73637265656e636173742d653061646130653833636438653766646361643834313637623866376666643765623565663835623063623839353766303363366630356264313662316365612e676966) + +[Provider]: https://pub.dev/packages/provider +[simple]: https://flutter.dev/docs/development/data-and-backend/state-mgmt/simple +[flutter.dev]: https://flutter.dev/ + +## Goals for this sample + +* Show simple use of `Provider` for providing an immutable value to a subtree +* Illustrate a simple state management approach using the ChangeNotifier class +* Show use of `ProxyProvider` for provided objects that depend on other provided objects + +## The important bits + +### `lib/main.dart` + +Here the app sets up objects it needs to track state: a catalog and a shopping cart. It builds +a `MultiProvider` to provide both objects at once to widgets further down the tree. + +The `CartModel` instance is provided using a `ChangeNotifierProxyProvider`, which combines +two types of functionality: + +1. It will automatically subscribe to changes in `CartModel` (if you only want this functionality + simply use `ChangeNotifierProvider`). +2. It takes the value of a previously provided object (in this case, `CatalogModel`, provided + just above), and uses it to build the value of `CartModel` (if you only want + _this_ functionality, simply use `ProxyProvider`). + +### `lib/models/*` + +This directory contains the model classes that are provided in `main.dart`. These classes +represent the app state. + +### `lib/screens/*` + +This directory contains widgets used to construct the two screens of the app: the catalog and +the cart. These widgets have access to the current state of both the catalog and the cart +via `Provider.of`. + +## Questions/issues + +If you have a general question about Provider, the best places to go are: + +* [Provider documentation](https://pub.dev/documentation/provider/latest/) +* [StackOverflow](https://stackoverflow.com/questions/tagged/flutter) + +If you run into an issue with the sample itself, please file an issue +in the [main Flutter repo](https://github.com/flutter/flutter/issues). diff --git a/flutter/realm_flutter/demo/android/.gitignore b/flutter/realm_flutter/demo/android/.gitignore new file mode 100755 index 000000000..0b9e049b0 --- /dev/null +++ b/flutter/realm_flutter/demo/android/.gitignore @@ -0,0 +1,7 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java diff --git a/flutter/realm_flutter/demo/android/app/build.gradle b/flutter/realm_flutter/demo/android/app/build.gradle new file mode 100755 index 000000000..4ee45f55a --- /dev/null +++ b/flutter/realm_flutter/demo/android/app/build.gradle @@ -0,0 +1,67 @@ +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterRoot = localProperties.getProperty('flutter.sdk') +if (flutterRoot == null) { + throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + +android { + compileSdkVersion 28 + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } + + lintOptions { + disable 'InvalidPackage' + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId "dev.flutter.provider_shopper" + minSdkVersion 16 + targetSdkVersion 28 + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig signingConfigs.debug + } + } +} + +flutter { + source '../..' +} + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + testImplementation 'junit:junit:4.12' + androidTestImplementation 'androidx.test:runner:1.1.1' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' +} diff --git a/flutter/realm_flutter/demo/android/app/src/debug/AndroidManifest.xml b/flutter/realm_flutter/demo/android/app/src/debug/AndroidManifest.xml new file mode 100755 index 000000000..8259b6e2d --- /dev/null +++ b/flutter/realm_flutter/demo/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/flutter/realm_flutter/demo/android/app/src/main/AndroidManifest.xml b/flutter/realm_flutter/demo/android/app/src/main/AndroidManifest.xml new file mode 100755 index 000000000..8fffa79b7 --- /dev/null +++ b/flutter/realm_flutter/demo/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + diff --git a/flutter/realm_flutter/demo/android/app/src/main/kotlin/dev/flutter/provider_shopper/MainActivity.kt b/flutter/realm_flutter/demo/android/app/src/main/kotlin/dev/flutter/provider_shopper/MainActivity.kt new file mode 100755 index 000000000..e0677952f --- /dev/null +++ b/flutter/realm_flutter/demo/android/app/src/main/kotlin/dev/flutter/provider_shopper/MainActivity.kt @@ -0,0 +1,24 @@ +package dev.flutter.provider_shopper + +import androidx.annotation.NonNull; +import io.flutter.embedding.android.FlutterActivity +import io.flutter.embedding.engine.FlutterEngine +import io.flutter.plugins.GeneratedPluginRegistrant + + +import android.os.Bundle + + +class MainActivity: FlutterActivity() { + override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { + GeneratedPluginRegistrant.registerWith(flutterEngine); + } + + + override fun onCreate(savedInstanceState: Bundle?) { + System.loadLibrary("realm_flutter"); + io.realm.realm_flutter.RealmFlutter.initRealm(applicationContext); + + super.onCreate(savedInstanceState) + } +} diff --git a/flutter/realm_flutter/demo/android/app/src/main/res/drawable/launch_background.xml b/flutter/realm_flutter/demo/android/app/src/main/res/drawable/launch_background.xml new file mode 100755 index 000000000..84037589b --- /dev/null +++ b/flutter/realm_flutter/demo/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/flutter/realm_flutter/demo/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/flutter/realm_flutter/demo/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100755 index 000000000..db77bb4b7 Binary files /dev/null and b/flutter/realm_flutter/demo/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/flutter/realm_flutter/demo/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/flutter/realm_flutter/demo/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100755 index 000000000..17987b79b Binary files /dev/null and b/flutter/realm_flutter/demo/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/flutter/realm_flutter/demo/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/flutter/realm_flutter/demo/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100755 index 000000000..09d439148 Binary files /dev/null and b/flutter/realm_flutter/demo/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/flutter/realm_flutter/demo/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/flutter/realm_flutter/demo/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100755 index 000000000..d5f1c8d34 Binary files /dev/null and b/flutter/realm_flutter/demo/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/flutter/realm_flutter/demo/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/flutter/realm_flutter/demo/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100755 index 000000000..4d6372eeb Binary files /dev/null and b/flutter/realm_flutter/demo/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/flutter/realm_flutter/demo/android/app/src/main/res/values/styles.xml b/flutter/realm_flutter/demo/android/app/src/main/res/values/styles.xml new file mode 100755 index 000000000..4c573225f --- /dev/null +++ b/flutter/realm_flutter/demo/android/app/src/main/res/values/styles.xml @@ -0,0 +1,8 @@ + + + + diff --git a/flutter/realm_flutter/demo/android/app/src/profile/AndroidManifest.xml b/flutter/realm_flutter/demo/android/app/src/profile/AndroidManifest.xml new file mode 100755 index 000000000..8259b6e2d --- /dev/null +++ b/flutter/realm_flutter/demo/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/flutter/realm_flutter/demo/android/build.gradle b/flutter/realm_flutter/demo/android/build.gradle new file mode 100755 index 000000000..7cad15687 --- /dev/null +++ b/flutter/realm_flutter/demo/android/build.gradle @@ -0,0 +1,31 @@ +buildscript { + ext.kotlin_version = '1.3.50' + repositories { + google() + jcenter() + } + + dependencies { + classpath 'com.android.tools.build:gradle:3.5.0' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +allprojects { + repositories { + google() + jcenter() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/flutter/realm_flutter/demo/android/gradle.properties b/flutter/realm_flutter/demo/android/gradle.properties new file mode 100755 index 000000000..74c1d924c --- /dev/null +++ b/flutter/realm_flutter/demo/android/gradle.properties @@ -0,0 +1,4 @@ +org.gradle.jvmargs=-Xmx1536M +android.enableR8=true +android.useAndroidX=true +android.enableJetifier=true diff --git a/flutter/realm_flutter/demo/android/gradle/wrapper/gradle-wrapper.properties b/flutter/realm_flutter/demo/android/gradle/wrapper/gradle-wrapper.properties new file mode 100755 index 000000000..31afd709e --- /dev/null +++ b/flutter/realm_flutter/demo/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Fri Jun 23 08:50:38 CEST 2017 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip diff --git a/flutter/realm_flutter/demo/android/settings.gradle b/flutter/realm_flutter/demo/android/settings.gradle new file mode 100755 index 000000000..4f14f8e57 --- /dev/null +++ b/flutter/realm_flutter/demo/android/settings.gradle @@ -0,0 +1,15 @@ +include ':app' + +def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() + +def plugins = new Properties() +def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') +if (pluginsFile.exists()) { + pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } +} + +plugins.each { name, path -> + def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() + include ":$name" + project(":$name").projectDir = pluginDirectory +} diff --git a/flutter/realm_flutter/demo/build.yaml b/flutter/realm_flutter/demo/build.yaml new file mode 100755 index 000000000..a6aa936f0 --- /dev/null +++ b/flutter/realm_flutter/demo/build.yaml @@ -0,0 +1,7 @@ +targets: + $default: + builders: + realm_generator|realm_object_builder: + enabled: true + generate_for: + - lib/models/*.dart \ No newline at end of file diff --git "a/flutter/realm_flutter/demo/build\\ios/Runner.build/Release-iphoneos/Runner.build/dgph" "b/flutter/realm_flutter/demo/build\\ios/Runner.build/Release-iphoneos/Runner.build/dgph" new file mode 100644 index 000000000..12f0fa255 Binary files /dev/null and "b/flutter/realm_flutter/demo/build\\ios/Runner.build/Release-iphoneos/Runner.build/dgph" differ diff --git a/flutter/realm_flutter/demo/fonts/Corben/Corben-Bold.ttf b/flutter/realm_flutter/demo/fonts/Corben/Corben-Bold.ttf new file mode 100755 index 000000000..47f401cdb Binary files /dev/null and b/flutter/realm_flutter/demo/fonts/Corben/Corben-Bold.ttf differ diff --git a/flutter/realm_flutter/demo/fonts/Corben/OFL.txt b/flutter/realm_flutter/demo/fonts/Corben/OFL.txt new file mode 100755 index 000000000..046fa8167 --- /dev/null +++ b/flutter/realm_flutter/demo/fonts/Corben/OFL.txt @@ -0,0 +1,94 @@ +Copyright (c) 2010, 2011 by vernon adams (vern@newtypography.co.uk), +with Reserved Font Name Corben. + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. \ No newline at end of file diff --git a/flutter/realm_flutter/demo/ios/.gitignore b/flutter/realm_flutter/demo/ios/.gitignore new file mode 100755 index 000000000..0f1df0fdd --- /dev/null +++ b/flutter/realm_flutter/demo/ios/.gitignore @@ -0,0 +1,32 @@ +*.mode1v3 +*.mode2v3 +*.moved-aside +*.pbxuser +*.perspectivev3 +**/*sync/ +.sconsign.dblite +.tags* +**/.vagrant/ +**/DerivedData/ +Icon? +**/Pods/ +**/.symlinks/ +profile +xcuserdata +**/.generated/ +Flutter/App.framework +Flutter/Flutter.framework +Flutter/Flutter.podspec +Flutter/Generated.xcconfig +Flutter/app.flx +Flutter/app.zip +Flutter/flutter_assets/ +Flutter/flutter_export_environment.sh +ServiceDefinitions.json +Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!default.mode1v3 +!default.mode2v3 +!default.pbxuser +!default.perspectivev3 diff --git a/flutter/realm_flutter/demo/ios/Flutter/.last_build_id b/flutter/realm_flutter/demo/ios/Flutter/.last_build_id new file mode 100644 index 000000000..7d4c76f9c --- /dev/null +++ b/flutter/realm_flutter/demo/ios/Flutter/.last_build_id @@ -0,0 +1 @@ +d1222013f0b750382da23733f26a214f \ No newline at end of file diff --git a/flutter/realm_flutter/demo/ios/Flutter/AppFrameworkInfo.plist b/flutter/realm_flutter/demo/ios/Flutter/AppFrameworkInfo.plist new file mode 100755 index 000000000..58e65f9b5 --- /dev/null +++ b/flutter/realm_flutter/demo/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + MinimumOSVersion + 8.0 + + diff --git a/flutter/realm_flutter/demo/ios/Flutter/Debug.xcconfig b/flutter/realm_flutter/demo/ios/Flutter/Debug.xcconfig new file mode 100755 index 000000000..c242b3d2d --- /dev/null +++ b/flutter/realm_flutter/demo/ios/Flutter/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "Generated.xcconfig" diff --git a/flutter/realm_flutter/demo/ios/Flutter/Release.xcconfig b/flutter/realm_flutter/demo/ios/Flutter/Release.xcconfig new file mode 100755 index 000000000..6f07b31e9 --- /dev/null +++ b/flutter/realm_flutter/demo/ios/Flutter/Release.xcconfig @@ -0,0 +1,2 @@ +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "Generated.xcconfig" diff --git a/flutter/realm_flutter/demo/ios/Podfile b/flutter/realm_flutter/demo/ios/Podfile new file mode 100644 index 000000000..1e8c3c90a --- /dev/null +++ b/flutter/realm_flutter/demo/ios/Podfile @@ -0,0 +1,41 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '9.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + end +end diff --git a/flutter/realm_flutter/demo/ios/Podfile.lock b/flutter/realm_flutter/demo/ios/Podfile.lock new file mode 100644 index 000000000..99c4f3d35 --- /dev/null +++ b/flutter/realm_flutter/demo/ios/Podfile.lock @@ -0,0 +1,22 @@ +PODS: + - Flutter (1.0.0) + - realm_flutter (0.0.1): + - Flutter + +DEPENDENCIES: + - Flutter (from `Flutter`) + - realm_flutter (from `.symlinks/plugins/realm_flutter/ios`) + +EXTERNAL SOURCES: + Flutter: + :path: Flutter + realm_flutter: + :path: ".symlinks/plugins/realm_flutter/ios" + +SPEC CHECKSUMS: + Flutter: 0e3d915762c693b495b44d77113d4970485de6ec + realm_flutter: c4fd555b998c00bd53e640522fbfd105c92b4fc1 + +PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c + +COCOAPODS: 1.8.4 diff --git a/flutter/realm_flutter/demo/ios/Runner.xcodeproj/project.pbxproj b/flutter/realm_flutter/demo/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 000000000..016dbe37e --- /dev/null +++ b/flutter/realm_flutter/demo/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,574 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + D66FDBFCFB625BA9EA2F5A66 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B79C0F551E5B9EE6B767CD27 /* Pods_Runner.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 03332EA5F7639526DD0C4B22 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 2E4B4B535E2DC76586A0D2CB /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 815019C2E0E01319F295BC2A /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + B79C0F551E5B9EE6B767CD27 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + D66FDBFCFB625BA9EA2F5A66 /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 0F7FCA0ABF15C30724B605A0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + B79C0F551E5B9EE6B767CD27 /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + 9D7AB6E064E85A7C6123DE26 /* Pods */, + 0F7FCA0ABF15C30724B605A0 /* Frameworks */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 97C146F11CF9000F007C117D /* Supporting Files */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, + ); + path = Runner; + sourceTree = ""; + }; + 97C146F11CF9000F007C117D /* Supporting Files */ = { + isa = PBXGroup; + children = ( + ); + name = "Supporting Files"; + sourceTree = ""; + }; + 9D7AB6E064E85A7C6123DE26 /* Pods */ = { + isa = PBXGroup; + children = ( + 815019C2E0E01319F295BC2A /* Pods-Runner.debug.xcconfig */, + 2E4B4B535E2DC76586A0D2CB /* Pods-Runner.release.xcconfig */, + 03332EA5F7639526DD0C4B22 /* Pods-Runner.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 2123E8E00F9FEEA9F64681FC /* [CP] Check Pods Manifest.lock */, + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + 4F2D50BD069CCFD53038F334 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1020; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 2123E8E00F9FEEA9F64681FC /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-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; + }; + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$PROJECT_DIR/.symlinks/plugins/realm_flutter/ios/scripts/xcode_backend.sh\" embed_and_thin"; + }; + 4F2D50BD069CCFD53038F334 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", + "${PODS_ROOT}/../Flutter/Flutter.framework", + "${BUILT_PRODUCTS_DIR}/realm_flutter/realm_flutter.framework", + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/realm_flutter.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$PROJECT_DIR/.symlinks/plugins/realm_flutter/ios/scripts/xcode_backend.sh\" build"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + 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_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + 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_IMPLICIT_RETAIN_SELF = 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_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "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 = gnu99; + 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 = 8.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/.symlinks/plugins/realm_flutter/ios/Frameworks/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/.symlinks/plugins/realm_flutter/ios/Frameworks/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.providerShopper; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + 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_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + 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_IMPLICIT_RETAIN_SELF = 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_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + 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 = 8.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + 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_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + 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_IMPLICIT_RETAIN_SELF = 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_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "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 = gnu99; + 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 = 8.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/.symlinks/plugins/realm_flutter/ios/Frameworks/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/.symlinks/plugins/realm_flutter/ios/Frameworks/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.providerShopper; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/.symlinks/plugins/realm_flutter/ios/Frameworks/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/.symlinks/plugins/realm_flutter/ios/Frameworks/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.providerShopper; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/flutter/realm_flutter/demo/ios/Runner.xcodeproj/project.pbxproj.bak b/flutter/realm_flutter/demo/ios/Runner.xcodeproj/project.pbxproj.bak new file mode 100644 index 000000000..016dbe37e --- /dev/null +++ b/flutter/realm_flutter/demo/ios/Runner.xcodeproj/project.pbxproj.bak @@ -0,0 +1,574 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + D66FDBFCFB625BA9EA2F5A66 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B79C0F551E5B9EE6B767CD27 /* Pods_Runner.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 03332EA5F7639526DD0C4B22 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 2E4B4B535E2DC76586A0D2CB /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 815019C2E0E01319F295BC2A /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + B79C0F551E5B9EE6B767CD27 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + D66FDBFCFB625BA9EA2F5A66 /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 0F7FCA0ABF15C30724B605A0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + B79C0F551E5B9EE6B767CD27 /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + 9D7AB6E064E85A7C6123DE26 /* Pods */, + 0F7FCA0ABF15C30724B605A0 /* Frameworks */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 97C146F11CF9000F007C117D /* Supporting Files */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, + ); + path = Runner; + sourceTree = ""; + }; + 97C146F11CF9000F007C117D /* Supporting Files */ = { + isa = PBXGroup; + children = ( + ); + name = "Supporting Files"; + sourceTree = ""; + }; + 9D7AB6E064E85A7C6123DE26 /* Pods */ = { + isa = PBXGroup; + children = ( + 815019C2E0E01319F295BC2A /* Pods-Runner.debug.xcconfig */, + 2E4B4B535E2DC76586A0D2CB /* Pods-Runner.release.xcconfig */, + 03332EA5F7639526DD0C4B22 /* Pods-Runner.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 2123E8E00F9FEEA9F64681FC /* [CP] Check Pods Manifest.lock */, + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + 4F2D50BD069CCFD53038F334 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1020; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 2123E8E00F9FEEA9F64681FC /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-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; + }; + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$PROJECT_DIR/.symlinks/plugins/realm_flutter/ios/scripts/xcode_backend.sh\" embed_and_thin"; + }; + 4F2D50BD069CCFD53038F334 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", + "${PODS_ROOT}/../Flutter/Flutter.framework", + "${BUILT_PRODUCTS_DIR}/realm_flutter/realm_flutter.framework", + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/realm_flutter.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$PROJECT_DIR/.symlinks/plugins/realm_flutter/ios/scripts/xcode_backend.sh\" build"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + 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_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + 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_IMPLICIT_RETAIN_SELF = 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_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "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 = gnu99; + 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 = 8.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/.symlinks/plugins/realm_flutter/ios/Frameworks/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/.symlinks/plugins/realm_flutter/ios/Frameworks/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.providerShopper; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + 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_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + 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_IMPLICIT_RETAIN_SELF = 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_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + 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 = 8.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + 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_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + 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_IMPLICIT_RETAIN_SELF = 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_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "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 = gnu99; + 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 = 8.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/.symlinks/plugins/realm_flutter/ios/Frameworks/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/.symlinks/plugins/realm_flutter/ios/Frameworks/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.providerShopper; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/.symlinks/plugins/realm_flutter/ios/Frameworks/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/.symlinks/plugins/realm_flutter/ios/Frameworks/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.providerShopper; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/flutter/realm_flutter/demo/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/flutter/realm_flutter/demo/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100755 index 000000000..59c6d3946 --- /dev/null +++ b/flutter/realm_flutter/demo/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/flutter/realm_flutter/demo/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/flutter/realm_flutter/demo/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100755 index 000000000..be0b92eff --- /dev/null +++ b/flutter/realm_flutter/demo/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/flutter/realm_flutter/demo/ios/Runner.xcworkspace/contents.xcworkspacedata b/flutter/realm_flutter/demo/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100755 index 000000000..21a3cc14c --- /dev/null +++ b/flutter/realm_flutter/demo/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/flutter/realm_flutter/demo/ios/Runner/AppDelegate.swift b/flutter/realm_flutter/demo/ios/Runner/AppDelegate.swift new file mode 100755 index 000000000..376368379 --- /dev/null +++ b/flutter/realm_flutter/demo/ios/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import UIKit +import Flutter + +@UIApplicationMain +@objc class AppDelegate: FlutterAppDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + GeneratedPluginRegistrant.register(with: self) + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } +} diff --git a/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100755 index 000000000..1950fd80e --- /dev/null +++ b/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,122 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "Icon-App-1024x1024@1x.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100755 index 000000000..dc9ada472 Binary files /dev/null and b/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ diff --git a/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100755 index 000000000..28c6bf030 Binary files /dev/null and b/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ diff --git a/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100755 index 000000000..2ccbfd967 Binary files /dev/null and b/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ diff --git a/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100755 index 000000000..f091b6b0b Binary files /dev/null and b/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ diff --git a/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100755 index 000000000..4cde12118 Binary files /dev/null and b/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ diff --git a/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100755 index 000000000..d0ef06e7e Binary files /dev/null and b/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ diff --git a/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png new file mode 100755 index 000000000..dcdc2306c Binary files /dev/null and b/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ diff --git a/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png new file mode 100755 index 000000000..2ccbfd967 Binary files /dev/null and b/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ diff --git a/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100755 index 000000000..c8f9ed8f5 Binary files /dev/null and b/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ diff --git a/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100755 index 000000000..a6d6b8609 Binary files /dev/null and b/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ diff --git a/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100755 index 000000000..a6d6b8609 Binary files /dev/null and b/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ diff --git a/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100755 index 000000000..75b2d164a Binary files /dev/null and b/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ diff --git a/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100755 index 000000000..c4df70d39 Binary files /dev/null and b/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ diff --git a/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png new file mode 100755 index 000000000..6a84f41e1 Binary files /dev/null and b/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ diff --git a/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100755 index 000000000..d0e1f5853 Binary files /dev/null and b/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ diff --git a/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json new file mode 100755 index 000000000..d08a4de32 --- /dev/null +++ b/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "LaunchImage.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png new file mode 100755 index 000000000..9da19eaca Binary files /dev/null and b/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ diff --git a/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100755 index 000000000..9da19eaca Binary files /dev/null and b/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ diff --git a/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100755 index 000000000..9da19eaca Binary files /dev/null and b/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ diff --git a/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100755 index 000000000..65a94b5db --- /dev/null +++ b/flutter/realm_flutter/demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -0,0 +1,5 @@ +# Launch Screen Assets + +You can customize the launch screen with your own desired assets by replacing the image files in this directory. + +You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/flutter/realm_flutter/demo/ios/Runner/Base.lproj/LaunchScreen.storyboard b/flutter/realm_flutter/demo/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100755 index 000000000..497371ea2 --- /dev/null +++ b/flutter/realm_flutter/demo/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/flutter/realm_flutter/demo/ios/Runner/Base.lproj/Main.storyboard b/flutter/realm_flutter/demo/ios/Runner/Base.lproj/Main.storyboard new file mode 100755 index 000000000..bbb83caae --- /dev/null +++ b/flutter/realm_flutter/demo/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/flutter/realm_flutter/demo/ios/Runner/Info.plist b/flutter/realm_flutter/demo/ios/Runner/Info.plist new file mode 100755 index 000000000..fc3591e52 --- /dev/null +++ b/flutter/realm_flutter/demo/ios/Runner/Info.plist @@ -0,0 +1,45 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + provider_shopper + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + + diff --git a/flutter/realm_flutter/demo/ios/Runner/Runner-Bridging-Header.h b/flutter/realm_flutter/demo/ios/Runner/Runner-Bridging-Header.h new file mode 100755 index 000000000..fae207f9e --- /dev/null +++ b/flutter/realm_flutter/demo/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/flutter/realm_flutter/demo/lib/common/theme.dart b/flutter/realm_flutter/demo/lib/common/theme.dart new file mode 100755 index 000000000..8bd7b9ecb --- /dev/null +++ b/flutter/realm_flutter/demo/lib/common/theme.dart @@ -0,0 +1,17 @@ +// Copyright 2019 The Flutter team. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/material.dart'; + +final appTheme = ThemeData( + primarySwatch: Colors.yellow, + textTheme: TextTheme( + headline1: TextStyle( + fontFamily: 'Corben', + fontWeight: FontWeight.w700, + fontSize: 24, + color: Colors.black, + ), + ), +); diff --git a/flutter/realm_flutter/demo/lib/main.dart b/flutter/realm_flutter/demo/lib/main.dart new file mode 100755 index 000000000..59eca7a79 --- /dev/null +++ b/flutter/realm_flutter/demo/lib/main.dart @@ -0,0 +1,83 @@ +// Copyright 2019 The Flutter team. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:ffi'; +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:provider_shopper/common/theme.dart'; +import 'package:provider_shopper/models/cart.dart'; +import 'package:provider_shopper/models/catalog.dart'; +import 'package:provider_shopper/screens/cart.dart'; +import 'package:provider_shopper/screens/catalog.dart'; +import 'package:provider_shopper/screens/login.dart'; + + +String _platformPath(String name, {String path}) { + if (path == null) path = ""; + if (Platform.isLinux || Platform.isAndroid) + return path + "lib" + name + ".so"; + if (Platform.isMacOS) return path + "lib" + name + ".dylib"; + if (Platform.isWindows) return path + name + ".dll"; + throw Exception("Platform not implemented"); +} + +DynamicLibrary dlopenPlatformSpecific(String name, {String path}) { + if (Platform.isIOS) { + final DynamicLibrary nativelib = DynamicLibrary.process(); + return nativelib; + } + + String fullPath = _platformPath(name, path: path); + return DynamicLibrary.open(fullPath); +} + +void main() { + print("Loading realm_flutter library"); + final testLibrary = dlopenPlatformSpecific("realm_flutter"); + print("finding the function"); + final initializeApi = testLibrary.lookupFunction< + IntPtr Function(Pointer), + int Function(Pointer)>("Dart_InitializeApiDL"); + + print(initializeApi(NativeApi.initializeApiDLData) == 0); + print("Running the app"); + + runApp(MyApp()); +} + +class MyApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + // Using MultiProvider is convenient when providing multiple objects. + return MultiProvider( + providers: [ + // In this sample app, CatalogModel never changes, so a simple Provider + // is sufficient. + Provider(create: (context) => CatalogModel()), + // CartModel is implemented as a ChangeNotifier, which calls for the use + // of ChangeNotifierProvider. Moreover, CartModel depends + // on CatalogModel, so a ProxyProvider is needed. + ChangeNotifierProxyProvider( + create: (context) => CartModel(), + update: (context, catalog, cart) { + cart.catalog = catalog; + return cart; + }, + ), + ], + child: MaterialApp( + title: 'Provider Demo', + theme: appTheme, + initialRoute: '/', + routes: { + '/': (context) => MyLogin(), + '/catalog': (context) => MyCatalog(), + '/cart': (context) => MyCart(), + }, + ), + ); + } +} diff --git a/flutter/realm_flutter/demo/lib/models/cart.dart b/flutter/realm_flutter/demo/lib/models/cart.dart new file mode 100755 index 000000000..7da9d0b9f --- /dev/null +++ b/flutter/realm_flutter/demo/lib/models/cart.dart @@ -0,0 +1,43 @@ +// Copyright 2019 The Flutter team. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/foundation.dart'; +import 'package:provider_shopper/models/catalog.dart'; + +class CartModel extends ChangeNotifier { + /// The private field backing [catalog]. + CatalogModel _catalog; + + /// Internal, private state of the cart. Stores the ids of each item. + final List _itemIds = []; + + /// The current catalog. Used to construct items from numeric ids. + CatalogModel get catalog => _catalog; + + set catalog(CatalogModel newCatalog) { + assert(newCatalog != null); + assert(_itemIds.every((id) => newCatalog.getById(id) != null), + 'The catalog $newCatalog does not have one of $_itemIds in it.'); + _catalog = newCatalog; + // Notify listeners, in case the new catalog provides information + // different from the previous one. For example, availability of an item + // might have changed. + notifyListeners(); + } + + /// List of items in the cart. + List get items => _itemIds.map((id) => _catalog.getById(id)).toList(); + + /// The current total price of all items. + int get totalPrice => + items.fold(0, (total, current) => total + current.price); + + /// Adds [item] to cart. This is the only way to modify the cart from outside. + void add(Item item) { + _itemIds.add(item.id); + // This line tells [Model] that it should rebuild the widgets that + // depend on it. + notifyListeners(); + } +} diff --git a/flutter/realm_flutter/demo/lib/models/catalog.dart b/flutter/realm_flutter/demo/lib/models/catalog.dart new file mode 100755 index 000000000..465876345 --- /dev/null +++ b/flutter/realm_flutter/demo/lib/models/catalog.dart @@ -0,0 +1,122 @@ +// Copyright 2019 The Flutter team. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/material.dart'; +import 'package:realm_flutter/realm.dart'; + + +//declare part file +part 'catalog.g.dart'; + + +/// A proxy of the catalog of items the user can buy. +/// +/// In a real app, this might be backed by a backend and cached on device. +/// In this sample app, the catalog is procedurally generated and infinite. +/// +/// For simplicity, the catalog is expected to be immutable (no products are +/// expected to be added, removed or changed during the execution of the app). +class CatalogModel { + Realm realm; + + CatalogModel() { + var config = new Configuration(); + config.schema.add(Item); + + realm = new Realm(config); + + var objects = realm.objects(); + + //if (objects.length == 0) { + realm.write(() { + realm.deleteAll(); + realm.create(Item()..id = 0..name = '123 Code-Smell'..price = 20); + realm.create(Item()..id = 1..name = '456 Control-Flow'..price = 1); + realm.create(Item()..id = 2..name = '789 Interpreter'..price = 2); + realm.create(Item()..id = 3..name = 'Recursion'..price = 3); + realm.create(Item()..id = 4..name = 'Sprint'..price = 4); + realm.create(Item()..id = 5..name = 'Heisenbug'..price = 5); + realm.create(Item()..id = 6..name = 'Spaghetti'..price = 6); + realm.create(Item()..id = 7..name = 'Hydra-Code'..price = 7); + realm.create(Item()..id = 8..name = 'Off-By-One'..price = 8); + realm.create(Item()..id = 9..name = 'Scope'..price = 9); + realm.create(Item()..id = 10..name = 'Callback'..price = 10); + realm.create(Item()..id = 11..name = 'Closure'..price = 11); + realm.create(Item()..id = 12..name = 'Automata'..price = 12); + realm.create(Item()..id = 13..name = 'Bit-Shift'..price = 13); + realm.create(Item()..id = 14..name = 'Currying'..price = 14); + }); + //} + } + + //creating this in a realm database + // static List itemNames = [ + // 'Code Smell', + // 'Control Flow', + // 'Interpreter', + // 'Recursion', + // 'Sprint', + // 'Heisenbug', + // 'Spaghetti', + // 'Hydra Code', + // 'Off-By-One', + // 'Scope', + // 'Callback', + // 'Closure', + // 'Automata', + // 'Bit Shift', + // 'Currying', + // ]; + + /// Get item by [id]. + /// + /// In this sample, the catalog is infinite, looping over [itemNames]. + Item getById(int id) // => Item(id, itemNames[id % itemNames.length]); + { + //realm objects in the database are not infinte. calculate the real object id + int objId = id % 14; + + var item = realm.find(objId); + return item; + } + + /// Get item by its position in the catalog. + Item getByPosition(int position) { + // In this simplified case, an item's position in the catalog + // is also its id. + return getById(position); + } +} + + +//creating realm object schema +class _Item { + @RealmProperty(primaryKey: true) + int id; + + @RealmProperty() + String name; + + @RealmProperty(defaultValue: '42') + int price; +} + +// @immutable +// class Item { +// final int id; +// final String name; +// final Color color; +// final int price = 42; + +// Item(this.id, this.name) +// // To make the sample app look nicer, each item is given one of the +// // Material Design primary colors. +// : color = Colors.primaries[id % Colors.primaries.length]; + +// @override +// int get hashCode => id; + +// @override +// bool operator ==(Object other) => other is Item && other.id == id; +// } diff --git a/flutter/realm_flutter/demo/lib/models/catalog.g.dart b/flutter/realm_flutter/demo/lib/models/catalog.g.dart new file mode 100755 index 000000000..6a249c448 --- /dev/null +++ b/flutter/realm_flutter/demo/lib/models/catalog.g.dart @@ -0,0 +1,32 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'catalog.dart'; + +// ************************************************************************** +// RealmObjectGenerator +// ************************************************************************** + +class Item extends RealmObject { + Item._constructor() : super.constructor(); + Item() {} + + @RealmProperty(primaryKey: true) + int get id => super['id']; + set id(int value) => super['id'] = value; + + @RealmProperty() + String get name => super['name']; + set name(String value) => super['name'] = value; + + @RealmProperty(defaultValue: '42') + int get price => super['price']; + set price(int value) => super['price'] = value; + + static dynamic getSchema() { + return RealmObject.getSchema('Item', [ + new SchemaProperty('id', type: 'int', primaryKey: true), + new SchemaProperty('name', type: 'string'), + new SchemaProperty('price', type: 'int', defaultValue: '42'), + ]); + } +} diff --git a/flutter/realm_flutter/demo/lib/screens/cart.dart b/flutter/realm_flutter/demo/lib/screens/cart.dart new file mode 100755 index 000000000..650cfdbd4 --- /dev/null +++ b/flutter/realm_flutter/demo/lib/screens/cart.dart @@ -0,0 +1,84 @@ +// Copyright 2019 The Flutter team. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:provider_shopper/models/cart.dart'; + +class MyCart extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('Cart', style: Theme.of(context).textTheme.headline1), + backgroundColor: Colors.white, + ), + body: Container( + color: Colors.yellow, + child: Column( + children: [ + Expanded( + child: Padding( + padding: const EdgeInsets.all(32), + child: _CartList(), + ), + ), + Divider(height: 4, color: Colors.black), + _CartTotal() + ], + ), + ), + ); + } +} + +class _CartList extends StatelessWidget { + @override + Widget build(BuildContext context) { + var itemNameStyle = Theme.of(context).textTheme.headline6; + var cart = Provider.of(context); + + return ListView.builder( + itemCount: cart.items.length, + itemBuilder: (context, index) => ListTile( + leading: Icon(Icons.done), + title: Text( + cart.items[index].name, + style: itemNameStyle, + ), + ), + ); + } +} + +class _CartTotal extends StatelessWidget { + @override + Widget build(BuildContext context) { + var hugeStyle = + Theme.of(context).textTheme.headline1.copyWith(fontSize: 48); + + return SizedBox( + height: 200, + child: Center( + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Consumer( + builder: (context, cart, child) => + Text('\$${cart.totalPrice}', style: hugeStyle)), + SizedBox(width: 24), + FlatButton( + onPressed: () { + Scaffold.of(context).showSnackBar( + SnackBar(content: Text('Buying not supported yet.'))); + }, + color: Colors.white, + child: Text('BUY'), + ), + ], + ), + ), + ); + } +} diff --git a/flutter/realm_flutter/demo/lib/screens/catalog.dart b/flutter/realm_flutter/demo/lib/screens/catalog.dart new file mode 100755 index 000000000..2dd021dcf --- /dev/null +++ b/flutter/realm_flutter/demo/lib/screens/catalog.dart @@ -0,0 +1,97 @@ +// Copyright 2019 The Flutter team. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:provider_shopper/models/cart.dart'; +import 'package:provider_shopper/models/catalog.dart'; + +class MyCatalog extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Scaffold( + body: CustomScrollView( + slivers: [ + _MyAppBar(), + SliverToBoxAdapter(child: SizedBox(height: 12)), + SliverList( + delegate: SliverChildBuilderDelegate( + (context, index) => _MyListItem(index)), + ), + ], + ), + ); + } +} + +class _AddButton extends StatelessWidget { + final Item item; + + const _AddButton({Key key, @required this.item}) : super(key: key); + + @override + Widget build(BuildContext context) { + var cart = Provider.of(context); + + return FlatButton( + onPressed: cart.items.contains(item) ? null : () => cart.add(item), + splashColor: Theme.of(context).primaryColor, + child: cart.items.contains(item) + ? Icon(Icons.check, semanticLabel: 'ADDED') + : Text('ADD'), + ); + } +} + +class _MyAppBar extends StatelessWidget { + @override + Widget build(BuildContext context) { + return SliverAppBar( + title: Text('Catalog', style: Theme.of(context).textTheme.headline1), + floating: true, + actions: [ + IconButton( + icon: Icon(Icons.shopping_cart), + onPressed: () => Navigator.pushNamed(context, '/cart'), + ), + ], + ); + } +} + +class _MyListItem extends StatelessWidget { + final int index; + + _MyListItem(this.index, {Key key}) : super(key: key); + + @override + Widget build(BuildContext context) { + var catalog = Provider.of(context); + var item = catalog.getByPosition(index); + var textTheme = Theme.of(context).textTheme.headline6; + + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + child: LimitedBox( + maxHeight: 48, + child: Row( + children: [ + AspectRatio( + aspectRatio: 1, + child: Container( + color: Colors.blue, + ), + ), + SizedBox(width: 24), + Expanded( + child: Text(item.name, style: textTheme), + ), + SizedBox(width: 24), + _AddButton(item: item), + ], + ), + ), + ); + } +} diff --git a/flutter/realm_flutter/demo/lib/screens/login.dart b/flutter/realm_flutter/demo/lib/screens/login.dart new file mode 100755 index 000000000..8c70539c2 --- /dev/null +++ b/flutter/realm_flutter/demo/lib/screens/login.dart @@ -0,0 +1,48 @@ +// Copyright 2020 The Flutter team. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/material.dart'; + +class MyLogin extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Scaffold( + body: Center( + child: Container( + padding: EdgeInsets.all(80.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'Welcome', + style: Theme.of(context).textTheme.headline1, + ), + TextFormField( + decoration: InputDecoration( + hintText: 'Username', + ), + ), + TextFormField( + decoration: InputDecoration( + hintText: 'Password', + ), + obscureText: true, + ), + SizedBox( + height: 24, + ), + RaisedButton( + color: Colors.yellow, + child: Text('ENTER'), + onPressed: () { + Navigator.pushReplacementNamed(context, '/catalog'); + }, + ) + ], + ), + ), + ), + ); + } +} diff --git a/flutter/realm_flutter/demo/macos/.gitignore b/flutter/realm_flutter/demo/macos/.gitignore new file mode 100755 index 000000000..e146f77e4 --- /dev/null +++ b/flutter/realm_flutter/demo/macos/.gitignore @@ -0,0 +1,6 @@ +# Flutter-related +**/Flutter/ephemeral/ +**/Pods/ + +# Xcode-related +**/xcuserdata/ diff --git a/flutter/realm_flutter/demo/macos/Flutter/Flutter-Debug.xcconfig b/flutter/realm_flutter/demo/macos/Flutter/Flutter-Debug.xcconfig new file mode 100755 index 000000000..bc0a0e38d --- /dev/null +++ b/flutter/realm_flutter/demo/macos/Flutter/Flutter-Debug.xcconfig @@ -0,0 +1,2 @@ +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/flutter/realm_flutter/demo/macos/Flutter/Flutter-Release.xcconfig b/flutter/realm_flutter/demo/macos/Flutter/Flutter-Release.xcconfig new file mode 100755 index 000000000..3bfddd246 --- /dev/null +++ b/flutter/realm_flutter/demo/macos/Flutter/Flutter-Release.xcconfig @@ -0,0 +1,2 @@ +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/flutter/realm_flutter/demo/macos/Flutter/GeneratedPluginRegistrant.swift b/flutter/realm_flutter/demo/macos/Flutter/GeneratedPluginRegistrant.swift new file mode 100755 index 000000000..d271b658e --- /dev/null +++ b/flutter/realm_flutter/demo/macos/Flutter/GeneratedPluginRegistrant.swift @@ -0,0 +1,10 @@ +// +// Generated file. Do not edit. +// + +import FlutterMacOS +import Foundation + + +func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { +} diff --git a/flutter/realm_flutter/demo/macos/Podfile b/flutter/realm_flutter/demo/macos/Podfile new file mode 100644 index 000000000..d60ec7102 --- /dev/null +++ b/flutter/realm_flutter/demo/macos/Podfile @@ -0,0 +1,82 @@ +platform :osx, '10.11' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def parse_KV_file(file, separator='=') + file_abs_path = File.expand_path(file) + if !File.exists? file_abs_path + return []; + end + pods_ary = [] + skip_line_start_symbols = ["#", "/"] + File.foreach(file_abs_path) { |line| + next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ } + plugin = line.split(pattern=separator) + if plugin.length == 2 + podname = plugin[0].strip() + path = plugin[1].strip() + podpath = File.expand_path("#{path}", file_abs_path) + pods_ary.push({:name => podname, :path => podpath}); + else + puts "Invalid plugin specification: #{line}" + end + } + return pods_ary +end + +def pubspec_supports_macos(file) + file_abs_path = File.expand_path(file) + if !File.exists? file_abs_path + return false; + end + File.foreach(file_abs_path) { |line| + return true if line =~ /^\s*macos:/ + } + return false +end + +target 'Runner' do + use_frameworks! + use_modular_headers! + + # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock + # referring to absolute paths on developers' machines. + ephemeral_dir = File.join('Flutter', 'ephemeral') + symlink_dir = File.join(ephemeral_dir, '.symlinks') + symlink_plugins_dir = File.join(symlink_dir, 'plugins') + system("rm -rf #{symlink_dir}") + system("mkdir -p #{symlink_plugins_dir}") + + # Flutter Pods + generated_xcconfig = parse_KV_file(File.join(ephemeral_dir, 'Flutter-Generated.xcconfig')) + if generated_xcconfig.empty? + puts "Flutter-Generated.xcconfig must exist. If you're running pod install manually, make sure flutter packages get is executed first." + end + generated_xcconfig.map { |p| + if p[:name] == 'FLUTTER_FRAMEWORK_DIR' + symlink = File.join(symlink_dir, 'flutter') + File.symlink(File.dirname(p[:path]), symlink) + pod 'FlutterMacOS', :path => File.join(symlink, File.basename(p[:path])) + end + } + + # Plugin Pods + plugin_pods = parse_KV_file('../.flutter-plugins') + plugin_pods.map { |p| + symlink = File.join(symlink_plugins_dir, p[:name]) + File.symlink(p[:path], symlink) + if pubspec_supports_macos(File.join(symlink, 'pubspec.yaml')) + pod p[:name], :path => File.join(symlink, 'macos') + end + } +end + +# Prevent Cocoapods from embedding a second Flutter framework and causing an error with the new Xcode build system. +install! 'cocoapods', :disable_input_output_paths => true diff --git a/flutter/realm_flutter/demo/macos/Runner.xcodeproj/project.pbxproj b/flutter/realm_flutter/demo/macos/Runner.xcodeproj/project.pbxproj new file mode 100755 index 000000000..5005f93ce --- /dev/null +++ b/flutter/realm_flutter/demo/macos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,596 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 51; + objects = { + +/* Begin PBXAggregateTarget section */ + 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; + buildPhases = ( + 33CC111E2044C6BF0003C045 /* ShellScript */, + ); + dependencies = ( + ); + name = "Flutter Assemble"; + productName = FLX; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; + 33D1A10422148B71006C7A3E /* FlutterMacOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 33D1A10322148B71006C7A3E /* FlutterMacOS.framework */; }; + 33D1A10522148B93006C7A3E /* FlutterMacOS.framework in Bundle Framework */ = {isa = PBXBuildFile; fileRef = 33D1A10322148B71006C7A3E /* FlutterMacOS.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + D73912F022F37F9E000D13A0 /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D73912EF22F37F9E000D13A0 /* App.framework */; }; + D73912F222F3801D000D13A0 /* App.framework in Bundle Framework */ = {isa = PBXBuildFile; fileRef = D73912EF22F37F9E000D13A0 /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC111A2044C6BA0003C045; + remoteInfo = FLX; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 33CC110E2044A8840003C045 /* Bundle Framework */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + D73912F222F3801D000D13A0 /* App.framework in Bundle Framework */, + 33D1A10522148B93006C7A3E /* FlutterMacOS.framework in Bundle Framework */, + ); + name = "Bundle Framework"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; + 33CC10ED2044A3C60003C045 /* provider_shopper.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "provider_shopper.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; + 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; + 33D1A10322148B71006C7A3E /* FlutterMacOS.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = FlutterMacOS.framework; path = Flutter/ephemeral/FlutterMacOS.framework; sourceTree = SOURCE_ROOT; }; + 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; + 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; + 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; + D73912EF22F37F9E000D13A0 /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/ephemeral/App.framework; sourceTree = SOURCE_ROOT; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 33CC10EA2044A3C60003C045 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + D73912F022F37F9E000D13A0 /* App.framework in Frameworks */, + 33D1A10422148B71006C7A3E /* FlutterMacOS.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 33BA886A226E78AF003329D5 /* Configs */ = { + isa = PBXGroup; + children = ( + 33E5194F232828860026EE4D /* AppInfo.xcconfig */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, + ); + path = Configs; + sourceTree = ""; + }; + 33CC10E42044A3C60003C045 = { + isa = PBXGroup; + children = ( + 33FAB671232836740065AC1E /* Runner */, + 33CEB47122A05771004F2AC0 /* Flutter */, + 33CC10EE2044A3C60003C045 /* Products */, + D73912EC22F37F3D000D13A0 /* Frameworks */, + ); + sourceTree = ""; + }; + 33CC10EE2044A3C60003C045 /* Products */ = { + isa = PBXGroup; + children = ( + 33CC10ED2044A3C60003C045 /* provider_shopper.app */, + ); + name = Products; + sourceTree = ""; + }; + 33CC11242044D66E0003C045 /* Resources */ = { + isa = PBXGroup; + children = ( + 33CC10F22044A3C60003C045 /* Assets.xcassets */, + 33CC10F42044A3C60003C045 /* MainMenu.xib */, + 33CC10F72044A3C60003C045 /* Info.plist */, + ); + name = Resources; + path = ..; + sourceTree = ""; + }; + 33CEB47122A05771004F2AC0 /* Flutter */ = { + isa = PBXGroup; + children = ( + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, + D73912EF22F37F9E000D13A0 /* App.framework */, + 33D1A10322148B71006C7A3E /* FlutterMacOS.framework */, + ); + path = Flutter; + sourceTree = ""; + }; + 33FAB671232836740065AC1E /* Runner */ = { + isa = PBXGroup; + children = ( + 33CC10F02044A3C60003C045 /* AppDelegate.swift */, + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, + 33E51913231747F40026EE4D /* DebugProfile.entitlements */, + 33E51914231749380026EE4D /* Release.entitlements */, + 33CC11242044D66E0003C045 /* Resources */, + 33BA886A226E78AF003329D5 /* Configs */, + ); + path = Runner; + sourceTree = ""; + }; + D73912EC22F37F3D000D13A0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 33CC10EC2044A3C60003C045 /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 33CC10E92044A3C60003C045 /* Sources */, + 33CC10EA2044A3C60003C045 /* Frameworks */, + 33CC10EB2044A3C60003C045 /* Resources */, + 33CC110E2044A8840003C045 /* Bundle Framework */, + 3399D490228B24CF009A79C7 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + 33CC11202044C79F0003C045 /* PBXTargetDependency */, + ); + name = Runner; + productName = Runner; + productReference = 33CC10ED2044A3C60003C045 /* provider_shopper.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 33CC10E52044A3C60003C045 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 0930; + ORGANIZATIONNAME = "The Flutter Authors"; + TargetAttributes = { + 33CC10EC2044A3C60003C045 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.Sandbox = { + enabled = 1; + }; + }; + }; + 33CC111A2044C6BA0003C045 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Manual; + }; + }; + }; + buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 8.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 33CC10E42044A3C60003C045; + productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 33CC10EC2044A3C60003C045 /* Runner */, + 33CC111A2044C6BA0003C045 /* Flutter Assemble */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 33CC10EB2044A3C60003C045 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3399D490228B24CF009A79C7 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename\n"; + }; + 33CC111E2044C6BF0003C045 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + Flutter/ephemeral/FlutterInputs.xcfilelist, + ); + inputPaths = ( + Flutter/ephemeral/tripwire, + ); + outputFileListPaths = ( + Flutter/ephemeral/FlutterOutputs.xcfilelist, + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh\ntouch Flutter/ephemeral/tripwire\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 33CC10E92044A3C60003C045 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; + targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 33CC10F52044A3C60003C045 /* Base */, + ); + name = MainMenu.xib; + path = Runner; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 338D0CE9231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + 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_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = 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_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + 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_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Profile; + }; + 338D0CEA231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter/ephemeral", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Profile; + }; + 338D0CEB231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Profile; + }; + 33CC10F92044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + 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_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = 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_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + 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_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 33CC10FA2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + 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_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = 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_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + 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_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + 33CC10FC2044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter/ephemeral", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 33CC10FD2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter/ephemeral", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 33CC111C2044C6BA0003C045 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 33CC111D2044C6BA0003C045 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10F92044A3C60003C045 /* Debug */, + 33CC10FA2044A3C60003C045 /* Release */, + 338D0CE9231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10FC2044A3C60003C045 /* Debug */, + 33CC10FD2044A3C60003C045 /* Release */, + 338D0CEA231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC111C2044C6BA0003C045 /* Debug */, + 33CC111D2044C6BA0003C045 /* Release */, + 338D0CEB231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 33CC10E52044A3C60003C045 /* Project object */; +} diff --git a/flutter/realm_flutter/demo/macos/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/flutter/realm_flutter/demo/macos/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100755 index 000000000..eb5d20679 --- /dev/null +++ b/flutter/realm_flutter/demo/macos/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/flutter/realm_flutter/demo/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/flutter/realm_flutter/demo/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100755 index 000000000..fc6bf8074 --- /dev/null +++ b/flutter/realm_flutter/demo/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/flutter/realm_flutter/demo/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/flutter/realm_flutter/demo/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100755 index 000000000..c517dfe2f --- /dev/null +++ b/flutter/realm_flutter/demo/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/flutter/realm_flutter/demo/macos/Runner.xcworkspace/contents.xcworkspacedata b/flutter/realm_flutter/demo/macos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100755 index 000000000..59c6d3946 --- /dev/null +++ b/flutter/realm_flutter/demo/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/flutter/realm_flutter/demo/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/flutter/realm_flutter/demo/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100755 index 000000000..fc6bf8074 --- /dev/null +++ b/flutter/realm_flutter/demo/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/flutter/realm_flutter/demo/macos/Runner/AppDelegate.swift b/flutter/realm_flutter/demo/macos/Runner/AppDelegate.swift new file mode 100755 index 000000000..553a135b0 --- /dev/null +++ b/flutter/realm_flutter/demo/macos/Runner/AppDelegate.swift @@ -0,0 +1,9 @@ +import Cocoa +import FlutterMacOS + +@NSApplicationMain +class AppDelegate: FlutterAppDelegate { + override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + return true + } +} diff --git a/flutter/realm_flutter/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/flutter/realm_flutter/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100755 index 000000000..8d4e7cb8e --- /dev/null +++ b/flutter/realm_flutter/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_16.png", + "scale" : "1x" + }, + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "2x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "1x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_64.png", + "scale" : "2x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_128.png", + "scale" : "1x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "2x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "1x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "2x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "1x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_1024.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/flutter/realm_flutter/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/flutter/realm_flutter/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png new file mode 100755 index 000000000..3c4935a7c Binary files /dev/null and b/flutter/realm_flutter/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png differ diff --git a/flutter/realm_flutter/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/flutter/realm_flutter/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png new file mode 100755 index 000000000..ed4cc1642 Binary files /dev/null and b/flutter/realm_flutter/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png differ diff --git a/flutter/realm_flutter/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/flutter/realm_flutter/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png new file mode 100755 index 000000000..483be6138 Binary files /dev/null and b/flutter/realm_flutter/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png differ diff --git a/flutter/realm_flutter/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/flutter/realm_flutter/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png new file mode 100755 index 000000000..bcbf36df2 Binary files /dev/null and b/flutter/realm_flutter/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png differ diff --git a/flutter/realm_flutter/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/flutter/realm_flutter/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png new file mode 100755 index 000000000..9c0a65286 Binary files /dev/null and b/flutter/realm_flutter/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png differ diff --git a/flutter/realm_flutter/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/flutter/realm_flutter/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png new file mode 100755 index 000000000..e71a72613 Binary files /dev/null and b/flutter/realm_flutter/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png differ diff --git a/flutter/realm_flutter/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/flutter/realm_flutter/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png new file mode 100755 index 000000000..8a31fe2dd Binary files /dev/null and b/flutter/realm_flutter/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png differ diff --git a/flutter/realm_flutter/demo/macos/Runner/Base.lproj/MainMenu.xib b/flutter/realm_flutter/demo/macos/Runner/Base.lproj/MainMenu.xib new file mode 100755 index 000000000..030024dc2 --- /dev/null +++ b/flutter/realm_flutter/demo/macos/Runner/Base.lproj/MainMenu.xib @@ -0,0 +1,339 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/flutter/realm_flutter/demo/macos/Runner/Configs/AppInfo.xcconfig b/flutter/realm_flutter/demo/macos/Runner/Configs/AppInfo.xcconfig new file mode 100755 index 000000000..8f6eb3ebd --- /dev/null +++ b/flutter/realm_flutter/demo/macos/Runner/Configs/AppInfo.xcconfig @@ -0,0 +1,14 @@ +// Application-level settings for the Runner target. +// +// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the +// future. If not, the values below would default to using the project name when this becomes a +// 'flutter create' template. + +// The application's name. By default this is also the title of the Flutter window. +PRODUCT_NAME = provider_shopper + +// The application's bundle identifier +PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.providerShopper + +// The copyright displayed in application information +PRODUCT_COPYRIGHT = Copyright © 2020 dev.flutter. All rights reserved. diff --git a/flutter/realm_flutter/demo/macos/Runner/Configs/Debug.xcconfig b/flutter/realm_flutter/demo/macos/Runner/Configs/Debug.xcconfig new file mode 100755 index 000000000..b39882372 --- /dev/null +++ b/flutter/realm_flutter/demo/macos/Runner/Configs/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Debug.xcconfig" +#include "Warnings.xcconfig" diff --git a/flutter/realm_flutter/demo/macos/Runner/Configs/Release.xcconfig b/flutter/realm_flutter/demo/macos/Runner/Configs/Release.xcconfig new file mode 100755 index 000000000..d93e5dc4a --- /dev/null +++ b/flutter/realm_flutter/demo/macos/Runner/Configs/Release.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Release.xcconfig" +#include "Warnings.xcconfig" diff --git a/flutter/realm_flutter/demo/macos/Runner/Configs/Warnings.xcconfig b/flutter/realm_flutter/demo/macos/Runner/Configs/Warnings.xcconfig new file mode 100755 index 000000000..fb4d7d3fb --- /dev/null +++ b/flutter/realm_flutter/demo/macos/Runner/Configs/Warnings.xcconfig @@ -0,0 +1,13 @@ +WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings +GCC_WARN_UNDECLARED_SELECTOR = YES +CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES +CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE +CLANG_WARN__DUPLICATE_METHOD_MATCH = YES +CLANG_WARN_PRAGMA_PACK = YES +CLANG_WARN_STRICT_PROTOTYPES = YES +CLANG_WARN_COMMA = YES +GCC_WARN_STRICT_SELECTOR_MATCH = YES +CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES +CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES +GCC_WARN_SHADOW = YES +CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/flutter/realm_flutter/demo/macos/Runner/DebugProfile.entitlements b/flutter/realm_flutter/demo/macos/Runner/DebugProfile.entitlements new file mode 100755 index 000000000..51d096708 --- /dev/null +++ b/flutter/realm_flutter/demo/macos/Runner/DebugProfile.entitlements @@ -0,0 +1,12 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.cs.allow-jit + + com.apple.security.network.server + + + diff --git a/flutter/realm_flutter/demo/macos/Runner/Info.plist b/flutter/realm_flutter/demo/macos/Runner/Info.plist new file mode 100755 index 000000000..3733c1a8e --- /dev/null +++ b/flutter/realm_flutter/demo/macos/Runner/Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + $(PRODUCT_COPYRIGHT) + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/flutter/realm_flutter/demo/macos/Runner/MainFlutterWindow.swift b/flutter/realm_flutter/demo/macos/Runner/MainFlutterWindow.swift new file mode 100755 index 000000000..4cb95dc98 --- /dev/null +++ b/flutter/realm_flutter/demo/macos/Runner/MainFlutterWindow.swift @@ -0,0 +1,15 @@ +import Cocoa +import FlutterMacOS + +class MainFlutterWindow: NSWindow { + override func awakeFromNib() { + let flutterViewController = FlutterViewController.init() + let windowFrame = self.frame + self.contentViewController = flutterViewController + self.setFrame(windowFrame, display: true) + + RegisterGeneratedPlugins(registry: flutterViewController) + + super.awakeFromNib() + } +} diff --git a/flutter/realm_flutter/demo/macos/Runner/Release.entitlements b/flutter/realm_flutter/demo/macos/Runner/Release.entitlements new file mode 100755 index 000000000..04336df3c --- /dev/null +++ b/flutter/realm_flutter/demo/macos/Runner/Release.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.security.app-sandbox + + + diff --git a/flutter/realm_flutter/demo/pubspec.lock b/flutter/realm_flutter/demo/pubspec.lock new file mode 100755 index 000000000..3002ac98b --- /dev/null +++ b/flutter/realm_flutter/demo/pubspec.lock @@ -0,0 +1,483 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + url: "https://pub.dartlang.org" + source: hosted + version: "0.39.10" + args: + dependency: transitive + description: + name: args + url: "https://pub.dartlang.org" + source: hosted + version: "1.6.0" + async: + dependency: transitive + description: + name: async + url: "https://pub.dartlang.org" + source: hosted + version: "2.5.0-nullsafety.1" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0-nullsafety.1" + build: + dependency: transitive + description: + name: build + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.0" + build_config: + dependency: transitive + description: + name: build_config + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.2" + build_daemon: + dependency: transitive + description: + name: build_daemon + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.4" + build_resolvers: + dependency: transitive + description: + name: build_resolvers + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.9" + build_runner: + dependency: "direct dev" + description: + name: build_runner + url: "https://pub.dartlang.org" + source: hosted + version: "1.10.0" + build_runner_core: + dependency: transitive + description: + name: build_runner_core + url: "https://pub.dartlang.org" + source: hosted + version: "5.2.0" + built_collection: + dependency: transitive + description: + name: built_collection + url: "https://pub.dartlang.org" + source: hosted + version: "4.3.2" + built_value: + dependency: transitive + description: + name: built_value + url: "https://pub.dartlang.org" + source: hosted + version: "7.1.0" + characters: + dependency: "direct main" + description: + name: characters + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0-nullsafety.3" + charcode: + dependency: transitive + description: + name: charcode + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0-nullsafety.1" + checked_yaml: + dependency: transitive + description: + name: checked_yaml + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" + clock: + dependency: transitive + description: + name: clock + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0-nullsafety.1" + code_builder: + dependency: transitive + description: + name: code_builder + url: "https://pub.dartlang.org" + source: hosted + version: "3.3.0" + collection: + dependency: transitive + description: + name: collection + url: "https://pub.dartlang.org" + source: hosted + version: "1.15.0-nullsafety.3" + convert: + dependency: transitive + description: + name: convert + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + crypto: + dependency: transitive + description: + name: crypto + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.4" + csslib: + dependency: transitive + description: + name: csslib + url: "https://pub.dartlang.org" + source: hosted + version: "0.16.1" + dart_style: + dependency: transitive + description: + name: dart_style + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.6" + fake_async: + dependency: transitive + description: + name: fake_async + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0-nullsafety.1" + fixnum: + dependency: transitive + description: + name: fixnum + url: "https://pub.dartlang.org" + source: hosted + version: "0.10.11" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + glob: + dependency: transitive + description: + name: glob + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + graphs: + dependency: transitive + description: + name: graphs + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.0" + html: + dependency: transitive + description: + name: html + url: "https://pub.dartlang.org" + source: hosted + version: "0.14.0+3" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.0" + http_parser: + dependency: transitive + description: + name: http_parser + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.4" + io: + dependency: transitive + description: + name: io + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.4" + js: + dependency: transitive + description: + name: js + url: "https://pub.dartlang.org" + source: hosted + version: "0.6.2" + json_annotation: + dependency: transitive + description: + name: json_annotation + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.1" + logging: + dependency: transitive + description: + name: logging + url: "https://pub.dartlang.org" + source: hosted + version: "0.11.4" + matcher: + dependency: transitive + description: + name: matcher + url: "https://pub.dartlang.org" + source: hosted + version: "0.12.10-nullsafety.1" + meta: + dependency: transitive + description: + name: meta + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.0-nullsafety.3" + mime: + dependency: transitive + description: + name: mime + url: "https://pub.dartlang.org" + source: hosted + version: "0.9.6+3" + nested: + dependency: transitive + description: + name: nested + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.4" + node_interop: + dependency: transitive + description: + name: node_interop + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.1" + node_io: + dependency: transitive + description: + name: node_io + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.1" + package_config: + dependency: transitive + description: + name: package_config + url: "https://pub.dartlang.org" + source: hosted + version: "1.9.3" + path: + dependency: transitive + description: + name: path + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.0-nullsafety.1" + pedantic: + dependency: "direct dev" + description: + name: pedantic + url: "https://pub.dartlang.org" + source: hosted + version: "1.9.0" + pool: + dependency: transitive + description: + name: pool + url: "https://pub.dartlang.org" + source: hosted + version: "1.4.0" + provider: + dependency: "direct main" + description: + name: provider + url: "https://pub.dartlang.org" + source: hosted + version: "4.1.3" + pub_semver: + dependency: transitive + description: + name: pub_semver + url: "https://pub.dartlang.org" + source: hosted + version: "1.4.4" + pubspec_parse: + dependency: transitive + description: + name: pubspec_parse + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.5" + quiver: + dependency: transitive + description: + name: quiver + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.3" + realm_flutter: + dependency: "direct main" + description: + path: ".." + relative: true + source: path + version: "0.0.1" + realm_generator: + dependency: transitive + description: + path: "../../../generator" + relative: true + source: path + version: "1.0.0" + shelf: + dependency: transitive + description: + name: shelf + url: "https://pub.dartlang.org" + source: hosted + version: "0.7.5" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.3" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_gen: + dependency: transitive + description: + name: source_gen + url: "https://pub.dartlang.org" + source: hosted + version: "0.9.5" + source_span: + dependency: transitive + description: + name: source_span + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.0-nullsafety.2" + stack_trace: + dependency: transitive + description: + name: stack_trace + url: "https://pub.dartlang.org" + source: hosted + version: "1.10.0-nullsafety.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0-nullsafety.1" + stream_transform: + dependency: transitive + description: + name: stream_transform + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + string_scanner: + dependency: transitive + description: + name: string_scanner + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0-nullsafety.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0-nullsafety.1" + test_api: + dependency: transitive + description: + name: test_api + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.19-nullsafety.2" + timing: + dependency: transitive + description: + name: timing + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.1+2" + typed_data: + dependency: transitive + description: + name: typed_data + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.0-nullsafety.3" + vector_math: + dependency: transitive + description: + name: vector_math + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0-nullsafety.3" + watcher: + dependency: transitive + description: + name: watcher + url: "https://pub.dartlang.org" + source: hosted + version: "0.9.7+15" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + yaml: + dependency: transitive + description: + name: yaml + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.1" +sdks: + dart: ">=2.10.0-110 <2.11.0" + flutter: ">=1.16.0" diff --git a/flutter/realm_flutter/demo/pubspec.yaml b/flutter/realm_flutter/demo/pubspec.yaml new file mode 100755 index 000000000..298b071d9 --- /dev/null +++ b/flutter/realm_flutter/demo/pubspec.yaml @@ -0,0 +1,34 @@ +name: provider_shopper +description: A shopping app sample that uses Provider for state management. + +version: 1.0.0+1 + +environment: + sdk: ">=2.5.0 <3.0.0" + +dependencies: + flutter: + sdk: flutter + + # Import the provider package. + provider: ^4.0.2 + + realm_flutter: + path: ../ + + characters: ^1.0.0 + +dev_dependencies: + flutter_test: + sdk: flutter + pedantic: ^1.9.0 + build_runner: 1.10.0 + +flutter: + uses-material-design: true + + fonts: + - family: Corben + fonts: + - asset: fonts/Corben/Corben-Bold.ttf + weight: 700 diff --git a/flutter/realm_flutter/demo/test/cart_widget_test.dart b/flutter/realm_flutter/demo/test/cart_widget_test.dart new file mode 100755 index 000000000..6cfce2c43 --- /dev/null +++ b/flutter/realm_flutter/demo/test/cart_widget_test.dart @@ -0,0 +1,62 @@ +// Copyright 2020 The Flutter team. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:provider/provider.dart'; +import 'package:provider_shopper/models/cart.dart'; +import 'package:provider_shopper/models/catalog.dart'; +import 'package:provider_shopper/screens/cart.dart'; + +CartModel cartModel; +CatalogModel catalogModel; +Widget createCartScreen() => MultiProvider( + providers: [ + Provider(create: (context) => CatalogModel()), + ChangeNotifierProxyProvider( + create: (context) => CartModel(), + update: (context, catalog, cart) { + catalogModel = catalog; + cartModel = cart; + cart.catalog = catalogModel; + return cart; + }, + ), + ], + child: MaterialApp( + home: MyCart(), + ), + ); + +void main() { + group('CartScreen widget tests', () { + testWidgets('Tapping BUY button displays snackbar.', (tester) async { + await tester.pumpWidget(createCartScreen()); + + // Verify no snackbar initially exists. + expect(find.byType(SnackBar), findsNothing); + await tester.tap(find.text('BUY')); + // Schedule animation. + await tester.pump(); + // Verifying the snackbar upon clicking the button. + expect(find.byType(SnackBar), findsOneWidget); + }); + + testWidgets('Testing when the cart contains items', (tester) async { + await tester.pumpWidget(createCartScreen()); + + // Adding five items in the cart and testing. + for (var i = 0; i < 5; i++) { + var item = catalogModel.getByPosition(i); + cartModel.add(item); + await tester.pumpAndSettle(); + expect(find.text(item.name), findsOneWidget); + } + + // Testing total price of the five items. + expect(find.text('\$${42 * 5}'), findsOneWidget); + expect(find.byIcon(Icons.done), findsNWidgets(5)); + }); + }); +} diff --git a/flutter/realm_flutter/demo/test/catalog_widget_test.dart b/flutter/realm_flutter/demo/test/catalog_widget_test.dart new file mode 100755 index 000000000..984c08527 --- /dev/null +++ b/flutter/realm_flutter/demo/test/catalog_widget_test.dart @@ -0,0 +1,58 @@ +// Copyright 2020 The Flutter team. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:provider/provider.dart'; +import 'package:provider_shopper/models/cart.dart'; +import 'package:provider_shopper/models/catalog.dart'; +import 'package:provider_shopper/screens/catalog.dart'; + +Widget createCatalogScreen() => MultiProvider( + providers: [ + Provider(create: (context) => CatalogModel()), + ChangeNotifierProxyProvider( + create: (context) => CartModel(), + update: (context, catalog, cart) { + cart.catalog = catalog; + return cart; + }, + ), + ], + child: MaterialApp( + home: MyCatalog(), + ), + ); + +void main() { + final catalogListItems = CatalogModel.itemNames.sublist(0, 3); + + group('CatalogScreen Widget Tests', () { + testWidgets('Testing item row counts and text', (tester) async { + await tester.pumpWidget(createCatalogScreen()); + + // Testing for the items on the screen after modifying + // the model for a fixed number of items. + for (var item in catalogListItems) { + expect(find.text(item), findsWidgets); + } + }); + + testWidgets('Testing the ADD buttons and check after clicking', + (tester) async { + await tester.pumpWidget(createCatalogScreen()); + + // Should find ADD buttons on the screen. + expect(find.text('ADD'), findsWidgets); + + // Performing the click on the ADD button of the first item in the list. + await tester.tap(find.widgetWithText(FlatButton, 'ADD').first); + await tester.pumpAndSettle(); + + // Verifying if the tapped ADD button has changed to the check icon. + expect(find.byIcon(Icons.check), findsOneWidget); + }); + }); +} diff --git a/flutter/realm_flutter/demo/test/login_widget_test.dart b/flutter/realm_flutter/demo/test/login_widget_test.dart new file mode 100755 index 000000000..ce09d91a7 --- /dev/null +++ b/flutter/realm_flutter/demo/test/login_widget_test.dart @@ -0,0 +1,41 @@ +// Copyright 2020 The Flutter team. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:provider/provider.dart'; +import 'package:provider_shopper/models/cart.dart'; +import 'package:provider_shopper/models/catalog.dart'; +import 'package:provider_shopper/screens/catalog.dart'; +import 'package:provider_shopper/screens/login.dart'; + +void main() { + testWidgets('Login page Widget test', (tester) async { + await tester.pumpWidget(MultiProvider( + providers: [ + Provider(create: (context) => CatalogModel()), + ChangeNotifierProxyProvider( + create: (context) => CartModel(), + update: (context, catalog, cart) { + cart.catalog = catalog; + return cart; + }, + ), + ], + child: MaterialApp( + initialRoute: '/', + routes: { + '/': (context) => MyLogin(), + '/catalog': (context) => MyCatalog(), + }, + ), + )); + + // Verifying the behaviour of ENTER button. + await tester.tap(find.text('ENTER')); + await tester.pumpAndSettle(); + + expect(find.text('Catalog'), findsOneWidget); + }); +} diff --git a/flutter/realm_flutter/demo/test/widget_test.dart b/flutter/realm_flutter/demo/test/widget_test.dart new file mode 100755 index 000000000..219c7003b --- /dev/null +++ b/flutter/realm_flutter/demo/test/widget_test.dart @@ -0,0 +1,33 @@ +// Copyright 2019 The Flutter team. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:provider_shopper/main.dart'; + +void main() { + testWidgets('smoke test', (tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(MyApp()); + + // Navigating through login page. + await tester.tap(find.text('ENTER')); + await tester.pumpAndSettle(); + + // Check that shopping cart is empty at start. + await tester.tap(find.byIcon(Icons.shopping_cart)); + await tester.pumpAndSettle(); + expect(find.text('\$0'), findsOneWidget); + + // Buy an item. + await tester.pageBack(); + await tester.pumpAndSettle(); + await tester.tap(find.text('ADD').first); + + // Check that the shopping cart is not empty anymore. + await tester.tap(find.byIcon(Icons.shopping_cart)); + await tester.pumpAndSettle(); + expect(find.text('\$0'), findsNothing); + }); +} diff --git a/flutter/realm_flutter/demo/web/icons/Icon-192.png b/flutter/realm_flutter/demo/web/icons/Icon-192.png new file mode 100755 index 000000000..b749bfef0 Binary files /dev/null and b/flutter/realm_flutter/demo/web/icons/Icon-192.png differ diff --git a/flutter/realm_flutter/demo/web/icons/Icon-512.png b/flutter/realm_flutter/demo/web/icons/Icon-512.png new file mode 100755 index 000000000..88cfd48df Binary files /dev/null and b/flutter/realm_flutter/demo/web/icons/Icon-512.png differ diff --git a/flutter/realm_flutter/demo/web/index.html b/flutter/realm_flutter/demo/web/index.html new file mode 100755 index 000000000..75ada35de --- /dev/null +++ b/flutter/realm_flutter/demo/web/index.html @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + provider_shopper + + + + + + diff --git a/flutter/realm_flutter/demo/web/manifest.json b/flutter/realm_flutter/demo/web/manifest.json new file mode 100755 index 000000000..98bce4ddf --- /dev/null +++ b/flutter/realm_flutter/demo/web/manifest.json @@ -0,0 +1,23 @@ +{ + "name": "provider_shopper", + "short_name": "provider_shopper", + "start_url": ".", + "display": "minimal-ui", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "A shopping app sample that uses Provider for state management.", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + } + ] +} diff --git a/flutter/realm_flutter/example/ios/Flutter/.last_build_id b/flutter/realm_flutter/example/ios/Flutter/.last_build_id new file mode 100644 index 000000000..6baf6539b --- /dev/null +++ b/flutter/realm_flutter/example/ios/Flutter/.last_build_id @@ -0,0 +1 @@ +2de5013308001d34cfd80d920f8b95a3 \ No newline at end of file diff --git a/flutter/realm_flutter/example/ios/Flutter/Debug.xcconfig b/flutter/realm_flutter/example/ios/Flutter/Debug.xcconfig index 592ceee85..e8efba114 100644 --- a/flutter/realm_flutter/example/ios/Flutter/Debug.xcconfig +++ b/flutter/realm_flutter/example/ios/Flutter/Debug.xcconfig @@ -1 +1,2 @@ +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include "Generated.xcconfig" diff --git a/flutter/realm_flutter/example/ios/Flutter/Release.xcconfig b/flutter/realm_flutter/example/ios/Flutter/Release.xcconfig index 592ceee85..399e9340e 100644 --- a/flutter/realm_flutter/example/ios/Flutter/Release.xcconfig +++ b/flutter/realm_flutter/example/ios/Flutter/Release.xcconfig @@ -1 +1,2 @@ +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #include "Generated.xcconfig" diff --git a/flutter/realm_flutter/example/ios/Podfile b/flutter/realm_flutter/example/ios/Podfile new file mode 100644 index 000000000..b29247c85 --- /dev/null +++ b/flutter/realm_flutter/example/ios/Podfile @@ -0,0 +1,41 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '9.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + end +end \ No newline at end of file diff --git a/flutter/realm_flutter/example/ios/Podfile.lock b/flutter/realm_flutter/example/ios/Podfile.lock new file mode 100644 index 000000000..838f6947c --- /dev/null +++ b/flutter/realm_flutter/example/ios/Podfile.lock @@ -0,0 +1,22 @@ +PODS: + - Flutter (1.0.0) + - realm_flutter (0.0.1): + - Flutter + +DEPENDENCIES: + - Flutter (from `Flutter`) + - realm_flutter (from `.symlinks/plugins/realm_flutter/ios`) + +EXTERNAL SOURCES: + Flutter: + :path: Flutter + realm_flutter: + :path: ".symlinks/plugins/realm_flutter/ios" + +SPEC CHECKSUMS: + Flutter: 0e3d915762c693b495b44d77113d4970485de6ec + realm_flutter: ce4c076adfa28dd3055fc6c6f046ac09314301da + +PODFILE CHECKSUM: fc81e398f362bae88bdf55239bd5cf842faad39f + +COCOAPODS: 1.8.4 diff --git a/flutter/realm_flutter/example/ios/Runner.xcodeproj/project.pbxproj b/flutter/realm_flutter/example/ios/Runner.xcodeproj/project.pbxproj index d496c0e34..4339ffeac 100644 --- a/flutter/realm_flutter/example/ios/Runner.xcodeproj/project.pbxproj +++ b/flutter/realm_flutter/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,13 +3,14 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 51; objects = { /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 8FDB4A92B87BF53CB502896F /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2CF015A00E3B1FC0B53E10D5 /* Pods_Runner.framework */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; @@ -31,7 +32,10 @@ /* Begin PBXFileReference section */ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 2AB9FF6A9603EE119CC9AE81 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 2CF015A00E3B1FC0B53E10D5 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 45A51A83E5B17426DC05B2BB /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; @@ -42,6 +46,7 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + A1EBEE253389B90FC001EB32 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -49,12 +54,21 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 8FDB4A92B87BF53CB502896F /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 817ADE2F8B3F7FE40352AD4B /* Frameworks */ = { + isa = PBXGroup; + children = ( + 2CF015A00E3B1FC0B53E10D5 /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( @@ -72,6 +86,8 @@ 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, + EDE658CEF79CDCA7C8BB35E4 /* Pods */, + 817ADE2F8B3F7FE40352AD4B /* Frameworks */, ); sourceTree = ""; }; @@ -106,6 +122,16 @@ name = "Supporting Files"; sourceTree = ""; }; + EDE658CEF79CDCA7C8BB35E4 /* Pods */ = { + isa = PBXGroup; + children = ( + 45A51A83E5B17426DC05B2BB /* Pods-Runner.debug.xcconfig */, + 2AB9FF6A9603EE119CC9AE81 /* Pods-Runner.release.xcconfig */, + A1EBEE253389B90FC001EB32 /* Pods-Runner.profile.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -113,12 +139,14 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( + 4DDD61A19F672129DE8FFF2E /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + 5F2B6B86B875A5ECA95D2F9A /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -189,7 +217,46 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + shellScript = "/bin/sh \"$PROJECT_DIR/.symlinks/plugins/realm_flutter/ios/scripts/xcode_backend.sh\" embed_and_thin\n"; + }; + 4DDD61A19F672129DE8FFF2E /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-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; + }; + 5F2B6B86B875A5ECA95D2F9A /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; @@ -203,7 +270,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + shellScript = "/bin/sh \"$PROJECT_DIR/.symlinks/plugins/realm_flutter/ios/scripts/xcode_backend.sh\" build\n"; }; /* End PBXShellScriptBuildPhase section */ @@ -241,7 +308,6 @@ /* Begin XCBuildConfiguration section */ 249021D3217E4FDB00AE95B9 /* Profile */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -300,13 +366,16 @@ ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", - "$(PROJECT_DIR)/Flutter", + "$(PROJECT_DIR)/.symlinks/plugins/realm_flutter/ios/Frameworks/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); LIBRARY_SEARCH_PATHS = ( "$(inherited)", - "$(PROJECT_DIR)/Flutter", + "$(PROJECT_DIR)/.symlinks/plugins/realm_flutter/ios/Frameworks/Flutter", ); PRODUCT_BUNDLE_IDENTIFIER = io.realm.realmFlutterExample; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -318,7 +387,6 @@ }; 97C147031CF9000F007C117D /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -374,7 +442,6 @@ }; 97C147041CF9000F007C117D /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -418,7 +485,8 @@ MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; @@ -434,13 +502,16 @@ ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", - "$(PROJECT_DIR)/Flutter", + "$(PROJECT_DIR)/.symlinks/plugins/realm_flutter/ios/Frameworks/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); LIBRARY_SEARCH_PATHS = ( "$(inherited)", - "$(PROJECT_DIR)/Flutter", + "$(PROJECT_DIR)/.symlinks/plugins/realm_flutter/ios/Frameworks/Flutter", ); PRODUCT_BUNDLE_IDENTIFIER = io.realm.realmFlutterExample; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -461,13 +532,16 @@ ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", - "$(PROJECT_DIR)/Flutter", + "$(PROJECT_DIR)/.symlinks/plugins/realm_flutter/ios/Frameworks/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); LIBRARY_SEARCH_PATHS = ( "$(inherited)", - "$(PROJECT_DIR)/Flutter", + "$(PROJECT_DIR)/.symlinks/plugins/realm_flutter/ios/Frameworks/Flutter", ); PRODUCT_BUNDLE_IDENTIFIER = io.realm.realmFlutterExample; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/flutter/realm_flutter/example/ios/Runner.xcodeproj/project.pbxproj.bak b/flutter/realm_flutter/example/ios/Runner.xcodeproj/project.pbxproj.bak new file mode 100644 index 000000000..4339ffeac --- /dev/null +++ b/flutter/realm_flutter/example/ios/Runner.xcodeproj/project.pbxproj.bak @@ -0,0 +1,580 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 51; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 8FDB4A92B87BF53CB502896F /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2CF015A00E3B1FC0B53E10D5 /* Pods_Runner.framework */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 2AB9FF6A9603EE119CC9AE81 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 2CF015A00E3B1FC0B53E10D5 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 45A51A83E5B17426DC05B2BB /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + A1EBEE253389B90FC001EB32 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 8FDB4A92B87BF53CB502896F /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 817ADE2F8B3F7FE40352AD4B /* Frameworks */ = { + isa = PBXGroup; + children = ( + 2CF015A00E3B1FC0B53E10D5 /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + EDE658CEF79CDCA7C8BB35E4 /* Pods */, + 817ADE2F8B3F7FE40352AD4B /* Frameworks */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 97C146F11CF9000F007C117D /* Supporting Files */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, + ); + path = Runner; + sourceTree = ""; + }; + 97C146F11CF9000F007C117D /* Supporting Files */ = { + isa = PBXGroup; + children = ( + ); + name = "Supporting Files"; + sourceTree = ""; + }; + EDE658CEF79CDCA7C8BB35E4 /* Pods */ = { + isa = PBXGroup; + children = ( + 45A51A83E5B17426DC05B2BB /* Pods-Runner.debug.xcconfig */, + 2AB9FF6A9603EE119CC9AE81 /* Pods-Runner.release.xcconfig */, + A1EBEE253389B90FC001EB32 /* Pods-Runner.profile.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 4DDD61A19F672129DE8FFF2E /* [CP] Check Pods Manifest.lock */, + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + 5F2B6B86B875A5ECA95D2F9A /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1020; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$PROJECT_DIR/.symlinks/plugins/realm_flutter/ios/scripts/xcode_backend.sh\" embed_and_thin\n"; + }; + 4DDD61A19F672129DE8FFF2E /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-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; + }; + 5F2B6B86B875A5ECA95D2F9A /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$PROJECT_DIR/.symlinks/plugins/realm_flutter/ios/scripts/xcode_backend.sh\" build\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + 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_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + 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_IMPLICIT_RETAIN_SELF = 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_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "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 = gnu99; + 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 = 8.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/.symlinks/plugins/realm_flutter/ios/Frameworks/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/.symlinks/plugins/realm_flutter/ios/Frameworks/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = io.realm.realmFlutterExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + 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_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + 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_IMPLICIT_RETAIN_SELF = 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_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + 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 = 8.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + 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_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + 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_IMPLICIT_RETAIN_SELF = 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_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "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 = gnu99; + 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 = 8.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/.symlinks/plugins/realm_flutter/ios/Frameworks/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/.symlinks/plugins/realm_flutter/ios/Frameworks/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = io.realm.realmFlutterExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/.symlinks/plugins/realm_flutter/ios/Frameworks/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/.symlinks/plugins/realm_flutter/ios/Frameworks/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = io.realm.realmFlutterExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/flutter/realm_flutter/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/flutter/realm_flutter/example/ios/Runner.xcworkspace/contents.xcworkspacedata index 1d526a16e..21a3cc14c 100644 --- a/flutter/realm_flutter/example/ios/Runner.xcworkspace/contents.xcworkspacedata +++ b/flutter/realm_flutter/example/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -4,4 +4,7 @@ + + diff --git a/flutter/realm_flutter/example/lib/main.dart b/flutter/realm_flutter/example/lib/main.dart index 7b374e775..e110303a6 100644 --- a/flutter/realm_flutter/example/lib/main.dart +++ b/flutter/realm_flutter/example/lib/main.dart @@ -25,6 +25,11 @@ String _platformPath(String name, {String path}) { } DynamicLibrary dlopenPlatformSpecific(String name, {String path}) { + if (Platform.isIOS) { + final DynamicLibrary nativelib = DynamicLibrary.process(); + return nativelib; + } + String fullPath = _platformPath(name, path: path); return DynamicLibrary.open(fullPath); } diff --git a/flutter/realm_flutter/example/pubspec.lock b/flutter/realm_flutter/example/pubspec.lock index f6d6ee94b..861b4836c 100644 --- a/flutter/realm_flutter/example/pubspec.lock +++ b/flutter/realm_flutter/example/pubspec.lock @@ -28,14 +28,14 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.4.2" + version: "2.5.0-nullsafety.1" boolean_selector: dependency: transitive description: name: boolean_selector url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.1.0-nullsafety.1" build: dependency: transitive description: @@ -98,14 +98,14 @@ packages: name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety" + version: "1.1.0-nullsafety.3" charcode: dependency: transitive description: name: charcode url: "https://pub.dartlang.org" source: hosted - version: "1.1.3" + version: "1.2.0-nullsafety.1" checked_yaml: dependency: transitive description: @@ -119,7 +119,7 @@ packages: name: clock url: "https://pub.dartlang.org" source: hosted - version: "1.0.1" + version: "1.1.0-nullsafety.1" code_builder: dependency: transitive description: @@ -133,7 +133,7 @@ packages: name: collection url: "https://pub.dartlang.org" source: hosted - version: "1.15.0-nullsafety" + version: "1.15.0-nullsafety.3" convert: dependency: transitive description: @@ -175,7 +175,7 @@ packages: name: fake_async url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.2.0-nullsafety.1" fixnum: dependency: transitive description: @@ -262,14 +262,14 @@ packages: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.8" + version: "0.12.10-nullsafety.1" meta: dependency: transitive description: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.3.0-nullsafety" + version: "1.3.0-nullsafety.3" mime: dependency: transitive description: @@ -304,7 +304,7 @@ packages: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.7.0" + version: "1.8.0-nullsafety.1" pedantic: dependency: transitive description: @@ -386,21 +386,21 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.7.0" + version: "1.8.0-nullsafety.2" stack_trace: dependency: transitive description: name: stack_trace url: "https://pub.dartlang.org" source: hosted - version: "1.9.5" + version: "1.10.0-nullsafety.1" stream_channel: dependency: transitive description: name: stream_channel url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.1.0-nullsafety.1" stream_transform: dependency: transitive description: @@ -414,21 +414,21 @@ packages: name: string_scanner url: "https://pub.dartlang.org" source: hosted - version: "1.0.5" + version: "1.1.0-nullsafety.1" term_glyph: dependency: transitive description: name: term_glyph url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.2.0-nullsafety.1" test_api: dependency: transitive description: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.17" + version: "0.2.19-nullsafety.2" timing: dependency: transitive description: @@ -442,14 +442,14 @@ packages: name: typed_data url: "https://pub.dartlang.org" source: hosted - version: "1.3.0-nullsafety" + version: "1.3.0-nullsafety.3" vector_math: dependency: transitive description: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety" + version: "2.1.0-nullsafety.3" watcher: dependency: transitive description: @@ -472,5 +472,5 @@ packages: source: hosted version: "2.2.1" sdks: - dart: ">=2.9.0-18.0 <2.9.0" + dart: ">=2.10.0-110 <2.11.0" flutter: ">=1.10.0" diff --git a/flutter/realm_flutter/example1/my_example13/.gitignore b/flutter/realm_flutter/example1/my_example13/.gitignore new file mode 100644 index 000000000..9d532b18a --- /dev/null +++ b/flutter/realm_flutter/example1/my_example13/.gitignore @@ -0,0 +1,41 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Web related +lib/generated_plugin_registrant.dart + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json diff --git a/flutter/realm_flutter/example1/my_example13/.metadata b/flutter/realm_flutter/example1/my_example13/.metadata new file mode 100644 index 000000000..24544cb74 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example13/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 84f3d28555368a70270e9ac8390a9441df95e752 + channel: stable + +project_type: app diff --git a/flutter/realm_flutter/example1/my_example13/README.md b/flutter/realm_flutter/example1/my_example13/README.md new file mode 100644 index 000000000..6fa631eaf --- /dev/null +++ b/flutter/realm_flutter/example1/my_example13/README.md @@ -0,0 +1,16 @@ +# my_example13 + +A new Flutter project. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) + +For help getting started with Flutter, view our +[online documentation](https://flutter.dev/docs), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/flutter/realm_flutter/example1/my_example13/android/.gitignore b/flutter/realm_flutter/example1/my_example13/android/.gitignore new file mode 100644 index 000000000..0a741cb43 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example13/android/.gitignore @@ -0,0 +1,11 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +key.properties diff --git a/flutter/realm_flutter/example1/my_example13/android/app/build.gradle b/flutter/realm_flutter/example1/my_example13/android/app/build.gradle new file mode 100644 index 000000000..abf8c296b --- /dev/null +++ b/flutter/realm_flutter/example1/my_example13/android/app/build.gradle @@ -0,0 +1,63 @@ +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterRoot = localProperties.getProperty('flutter.sdk') +if (flutterRoot == null) { + throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + +android { + compileSdkVersion 29 + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } + + lintOptions { + disable 'InvalidPackage' + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId "com.example.my_example13" + minSdkVersion 16 + targetSdkVersion 29 + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig signingConfigs.debug + } + } +} + +flutter { + source '../..' +} + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" +} diff --git a/flutter/realm_flutter/example1/my_example13/android/app/src/debug/AndroidManifest.xml b/flutter/realm_flutter/example1/my_example13/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 000000000..c82ab8f36 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example13/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/flutter/realm_flutter/example1/my_example13/android/app/src/main/AndroidManifest.xml b/flutter/realm_flutter/example1/my_example13/android/app/src/main/AndroidManifest.xml new file mode 100644 index 000000000..22d781b72 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example13/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + diff --git a/flutter/realm_flutter/example1/my_example13/android/app/src/main/kotlin/com/example/my_example13/MainActivity.kt b/flutter/realm_flutter/example1/my_example13/android/app/src/main/kotlin/com/example/my_example13/MainActivity.kt new file mode 100644 index 000000000..07972f951 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example13/android/app/src/main/kotlin/com/example/my_example13/MainActivity.kt @@ -0,0 +1,6 @@ +package com.example.my_example13 + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() { +} diff --git a/flutter/realm_flutter/example1/my_example13/android/app/src/main/res/drawable/launch_background.xml b/flutter/realm_flutter/example1/my_example13/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 000000000..304732f88 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example13/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/flutter/realm_flutter/example1/my_example13/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/flutter/realm_flutter/example1/my_example13/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..db77bb4b7 Binary files /dev/null and b/flutter/realm_flutter/example1/my_example13/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/flutter/realm_flutter/example1/my_example13/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/flutter/realm_flutter/example1/my_example13/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..17987b79b Binary files /dev/null and b/flutter/realm_flutter/example1/my_example13/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/flutter/realm_flutter/example1/my_example13/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/flutter/realm_flutter/example1/my_example13/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..09d439148 Binary files /dev/null and b/flutter/realm_flutter/example1/my_example13/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/flutter/realm_flutter/example1/my_example13/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/flutter/realm_flutter/example1/my_example13/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..d5f1c8d34 Binary files /dev/null and b/flutter/realm_flutter/example1/my_example13/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/flutter/realm_flutter/example1/my_example13/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/flutter/realm_flutter/example1/my_example13/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..4d6372eeb Binary files /dev/null and b/flutter/realm_flutter/example1/my_example13/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/flutter/realm_flutter/example1/my_example13/android/app/src/main/res/values/styles.xml b/flutter/realm_flutter/example1/my_example13/android/app/src/main/res/values/styles.xml new file mode 100644 index 000000000..1f83a33fd --- /dev/null +++ b/flutter/realm_flutter/example1/my_example13/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/flutter/realm_flutter/example1/my_example13/android/app/src/profile/AndroidManifest.xml b/flutter/realm_flutter/example1/my_example13/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 000000000..c82ab8f36 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example13/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/flutter/realm_flutter/example1/my_example13/android/build.gradle b/flutter/realm_flutter/example1/my_example13/android/build.gradle new file mode 100644 index 000000000..3100ad2d5 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example13/android/build.gradle @@ -0,0 +1,31 @@ +buildscript { + ext.kotlin_version = '1.3.50' + repositories { + google() + jcenter() + } + + dependencies { + classpath 'com.android.tools.build:gradle:3.5.0' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +allprojects { + repositories { + google() + jcenter() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/flutter/realm_flutter/example1/my_example13/android/gradle.properties b/flutter/realm_flutter/example1/my_example13/android/gradle.properties new file mode 100644 index 000000000..94adc3a3f --- /dev/null +++ b/flutter/realm_flutter/example1/my_example13/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx1536M +android.useAndroidX=true +android.enableJetifier=true diff --git a/flutter/realm_flutter/example1/my_example13/android/gradle/wrapper/gradle-wrapper.properties b/flutter/realm_flutter/example1/my_example13/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..296b146b7 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example13/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Fri Jun 23 08:50:38 CEST 2017 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip diff --git a/flutter/realm_flutter/example1/my_example13/android/settings.gradle b/flutter/realm_flutter/example1/my_example13/android/settings.gradle new file mode 100644 index 000000000..44e62bcf0 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example13/android/settings.gradle @@ -0,0 +1,11 @@ +include ':app' + +def localPropertiesFile = new File(rootProject.projectDir, "local.properties") +def properties = new Properties() + +assert localPropertiesFile.exists() +localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } + +def flutterSdkPath = properties.getProperty("flutter.sdk") +assert flutterSdkPath != null, "flutter.sdk not set in local.properties" +apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/flutter/realm_flutter/example1/my_example13/ios/.gitignore b/flutter/realm_flutter/example1/my_example13/ios/.gitignore new file mode 100644 index 000000000..e96ef602b --- /dev/null +++ b/flutter/realm_flutter/example1/my_example13/ios/.gitignore @@ -0,0 +1,32 @@ +*.mode1v3 +*.mode2v3 +*.moved-aside +*.pbxuser +*.perspectivev3 +**/*sync/ +.sconsign.dblite +.tags* +**/.vagrant/ +**/DerivedData/ +Icon? +**/Pods/ +**/.symlinks/ +profile +xcuserdata +**/.generated/ +Flutter/App.framework +Flutter/Flutter.framework +Flutter/Flutter.podspec +Flutter/Generated.xcconfig +Flutter/app.flx +Flutter/app.zip +Flutter/flutter_assets/ +Flutter/flutter_export_environment.sh +ServiceDefinitions.json +Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!default.mode1v3 +!default.mode2v3 +!default.pbxuser +!default.perspectivev3 diff --git a/flutter/realm_flutter/example1/my_example13/ios/Flutter/AppFrameworkInfo.plist b/flutter/realm_flutter/example1/my_example13/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 000000000..f2872cf47 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example13/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + MinimumOSVersion + 9.0 + + diff --git a/flutter/realm_flutter/example1/my_example13/ios/Flutter/Debug.xcconfig b/flutter/realm_flutter/example1/my_example13/ios/Flutter/Debug.xcconfig new file mode 100644 index 000000000..e8efba114 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example13/ios/Flutter/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "Generated.xcconfig" diff --git a/flutter/realm_flutter/example1/my_example13/ios/Flutter/Release.xcconfig b/flutter/realm_flutter/example1/my_example13/ios/Flutter/Release.xcconfig new file mode 100644 index 000000000..399e9340e --- /dev/null +++ b/flutter/realm_flutter/example1/my_example13/ios/Flutter/Release.xcconfig @@ -0,0 +1,2 @@ +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "Generated.xcconfig" diff --git a/flutter/realm_flutter/example1/my_example13/ios/Podfile b/flutter/realm_flutter/example1/my_example13/ios/Podfile new file mode 100644 index 000000000..1e8c3c90a --- /dev/null +++ b/flutter/realm_flutter/example1/my_example13/ios/Podfile @@ -0,0 +1,41 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '9.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + end +end diff --git a/flutter/realm_flutter/example1/my_example13/ios/Podfile.lock b/flutter/realm_flutter/example1/my_example13/ios/Podfile.lock new file mode 100644 index 000000000..7a34a8540 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example13/ios/Podfile.lock @@ -0,0 +1,22 @@ +PODS: + - Flutter (1.0.0) + - realm_flutter (0.0.1): + - Flutter + +DEPENDENCIES: + - Flutter (from `Flutter`) + - realm_flutter (from `.symlinks/plugins/realm_flutter/ios`) + +EXTERNAL SOURCES: + Flutter: + :path: Flutter + realm_flutter: + :path: ".symlinks/plugins/realm_flutter/ios" + +SPEC CHECKSUMS: + Flutter: 0e3d915762c693b495b44d77113d4970485de6ec + realm_flutter: e49a36db9a7c584067cba36d9c5d4f67ba19485e + +PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c + +COCOAPODS: 1.8.4 diff --git a/flutter/realm_flutter/example1/my_example13/ios/Runner.xcodeproj/project.pbxproj b/flutter/realm_flutter/example1/my_example13/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 000000000..9690eb419 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example13/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,566 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + B9ED228D6B5155D6E1CC14A3 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 55BFF4B5BA7D04E32C27F26C /* Pods_Runner.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 34E6A8E6FC9E441ACC5BFFAD /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 55BFF4B5BA7D04E32C27F26C /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 90699110A68BEC73ECDAC590 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + A4A2382E56844966F5066520 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + B9ED228D6B5155D6E1CC14A3 /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 663DBF08D3FE246B9602B42B /* Pods */ = { + isa = PBXGroup; + children = ( + 90699110A68BEC73ECDAC590 /* Pods-Runner.debug.xcconfig */, + A4A2382E56844966F5066520 /* Pods-Runner.release.xcconfig */, + 34E6A8E6FC9E441ACC5BFFAD /* Pods-Runner.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + 663DBF08D3FE246B9602B42B /* Pods */, + A32C3994602E9B502AC46BE5 /* Frameworks */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, + ); + path = Runner; + sourceTree = ""; + }; + A32C3994602E9B502AC46BE5 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 55BFF4B5BA7D04E32C27F26C /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + B6E8AB7D7B8235E25956EB9F /* [CP] Check Pods Manifest.lock */, + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + 963B47F1B98540996C04772B /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1020; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$PROJECT_DIR/.symlinks/plugins/realm_flutter/ios/scripts/xcode_backend.sh\" embed_and_thin"; + }; + 963B47F1B98540996C04772B /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", + "${PODS_ROOT}/../Flutter/Flutter.framework", + "${BUILT_PRODUCTS_DIR}/realm_flutter/realm_flutter.framework", + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/realm_flutter.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$PROJECT_DIR/.symlinks/plugins/realm_flutter/ios/scripts/xcode_backend.sh\" build"; + }; + B6E8AB7D7B8235E25956EB9F /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-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; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + 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_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + 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_IMPLICIT_RETAIN_SELF = 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_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "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 = gnu99; + 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 = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/.symlinks/plugins/realm_flutter/ios/Frameworks/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/.symlinks/plugins/realm_flutter/ios/Frameworks/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.myExample13; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + 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_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + 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_IMPLICIT_RETAIN_SELF = 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_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + 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 = 9.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + 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_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + 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_IMPLICIT_RETAIN_SELF = 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_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "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 = gnu99; + 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 = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/.symlinks/plugins/realm_flutter/ios/Frameworks/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/.symlinks/plugins/realm_flutter/ios/Frameworks/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.myExample13; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/.symlinks/plugins/realm_flutter/ios/Frameworks/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/.symlinks/plugins/realm_flutter/ios/Frameworks/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.myExample13; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/flutter/realm_flutter/example1/my_example13/ios/Runner.xcodeproj/project.pbxproj.bak b/flutter/realm_flutter/example1/my_example13/ios/Runner.xcodeproj/project.pbxproj.bak new file mode 100644 index 000000000..9690eb419 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example13/ios/Runner.xcodeproj/project.pbxproj.bak @@ -0,0 +1,566 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + B9ED228D6B5155D6E1CC14A3 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 55BFF4B5BA7D04E32C27F26C /* Pods_Runner.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 34E6A8E6FC9E441ACC5BFFAD /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 55BFF4B5BA7D04E32C27F26C /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 90699110A68BEC73ECDAC590 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + A4A2382E56844966F5066520 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + B9ED228D6B5155D6E1CC14A3 /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 663DBF08D3FE246B9602B42B /* Pods */ = { + isa = PBXGroup; + children = ( + 90699110A68BEC73ECDAC590 /* Pods-Runner.debug.xcconfig */, + A4A2382E56844966F5066520 /* Pods-Runner.release.xcconfig */, + 34E6A8E6FC9E441ACC5BFFAD /* Pods-Runner.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + 663DBF08D3FE246B9602B42B /* Pods */, + A32C3994602E9B502AC46BE5 /* Frameworks */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, + ); + path = Runner; + sourceTree = ""; + }; + A32C3994602E9B502AC46BE5 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 55BFF4B5BA7D04E32C27F26C /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + B6E8AB7D7B8235E25956EB9F /* [CP] Check Pods Manifest.lock */, + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + 963B47F1B98540996C04772B /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1020; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$PROJECT_DIR/.symlinks/plugins/realm_flutter/ios/scripts/xcode_backend.sh\" embed_and_thin"; + }; + 963B47F1B98540996C04772B /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", + "${PODS_ROOT}/../Flutter/Flutter.framework", + "${BUILT_PRODUCTS_DIR}/realm_flutter/realm_flutter.framework", + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/realm_flutter.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$PROJECT_DIR/.symlinks/plugins/realm_flutter/ios/scripts/xcode_backend.sh\" build"; + }; + B6E8AB7D7B8235E25956EB9F /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-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; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + 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_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + 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_IMPLICIT_RETAIN_SELF = 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_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "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 = gnu99; + 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 = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/.symlinks/plugins/realm_flutter/ios/Frameworks/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/.symlinks/plugins/realm_flutter/ios/Frameworks/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.myExample13; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + 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_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + 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_IMPLICIT_RETAIN_SELF = 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_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + 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 = 9.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + 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_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + 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_IMPLICIT_RETAIN_SELF = 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_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "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 = gnu99; + 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 = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/.symlinks/plugins/realm_flutter/ios/Frameworks/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/.symlinks/plugins/realm_flutter/ios/Frameworks/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.myExample13; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/.symlinks/plugins/realm_flutter/ios/Frameworks/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/.symlinks/plugins/realm_flutter/ios/Frameworks/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.myExample13; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/flutter/realm_flutter/example1/my_example13/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/flutter/realm_flutter/example1/my_example13/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..1d526a16e --- /dev/null +++ b/flutter/realm_flutter/example1/my_example13/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/flutter/realm_flutter/example1/my_example13/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/flutter/realm_flutter/example1/my_example13/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example13/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/flutter/realm_flutter/example1/my_example13/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/flutter/realm_flutter/example1/my_example13/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 000000000..f9b0d7c5e --- /dev/null +++ b/flutter/realm_flutter/example1/my_example13/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/flutter/realm_flutter/example1/my_example13/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/flutter/realm_flutter/example1/my_example13/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 000000000..a28140cfd --- /dev/null +++ b/flutter/realm_flutter/example1/my_example13/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/flutter/realm_flutter/example1/my_example13/ios/Runner.xcworkspace/contents.xcworkspacedata b/flutter/realm_flutter/example1/my_example13/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..21a3cc14c --- /dev/null +++ b/flutter/realm_flutter/example1/my_example13/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/flutter/realm_flutter/example1/my_example13/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/flutter/realm_flutter/example1/my_example13/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example13/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/flutter/realm_flutter/example1/my_example13/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/flutter/realm_flutter/example1/my_example13/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 000000000..f9b0d7c5e --- /dev/null +++ b/flutter/realm_flutter/example1/my_example13/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/flutter/realm_flutter/example1/my_example13/ios/Runner/AppDelegate.swift b/flutter/realm_flutter/example1/my_example13/ios/Runner/AppDelegate.swift new file mode 100644 index 000000000..70693e4a8 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example13/ios/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import UIKit +import Flutter + +@UIApplicationMain +@objc class AppDelegate: FlutterAppDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + GeneratedPluginRegistrant.register(with: self) + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } +} diff --git a/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000..d36b1fab2 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,122 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "Icon-App-1024x1024@1x.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 000000000..dc9ada472 Binary files /dev/null and b/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ diff --git a/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100644 index 000000000..28c6bf030 Binary files /dev/null and b/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ diff --git a/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 000000000..2ccbfd967 Binary files /dev/null and b/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ diff --git a/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 000000000..f091b6b0b Binary files /dev/null and b/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ diff --git a/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 000000000..4cde12118 Binary files /dev/null and b/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ diff --git a/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 000000000..d0ef06e7e Binary files /dev/null and b/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ diff --git a/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png new file mode 100644 index 000000000..dcdc2306c Binary files /dev/null and b/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ diff --git a/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png new file mode 100644 index 000000000..2ccbfd967 Binary files /dev/null and b/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ diff --git a/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 000000000..c8f9ed8f5 Binary files /dev/null and b/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ diff --git a/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 000000000..a6d6b8609 Binary files /dev/null and b/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ diff --git a/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 000000000..a6d6b8609 Binary files /dev/null and b/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ diff --git a/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 000000000..75b2d164a Binary files /dev/null and b/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ diff --git a/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 000000000..c4df70d39 Binary files /dev/null and b/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ diff --git a/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png new file mode 100644 index 000000000..6a84f41e1 Binary files /dev/null and b/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ diff --git a/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 000000000..d0e1f5853 Binary files /dev/null and b/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ diff --git a/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json new file mode 100644 index 000000000..0bedcf2fd --- /dev/null +++ b/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "LaunchImage.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png new file mode 100644 index 000000000..9da19eaca Binary files /dev/null and b/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ diff --git a/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 000000000..9da19eaca Binary files /dev/null and b/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ diff --git a/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 000000000..9da19eaca Binary files /dev/null and b/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ diff --git a/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 000000000..89c2725b7 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example13/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -0,0 +1,5 @@ +# Launch Screen Assets + +You can customize the launch screen with your own desired assets by replacing the image files in this directory. + +You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/flutter/realm_flutter/example1/my_example13/ios/Runner/Base.lproj/LaunchScreen.storyboard b/flutter/realm_flutter/example1/my_example13/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 000000000..f2e259c7c --- /dev/null +++ b/flutter/realm_flutter/example1/my_example13/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/flutter/realm_flutter/example1/my_example13/ios/Runner/Base.lproj/Main.storyboard b/flutter/realm_flutter/example1/my_example13/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 000000000..f3c28516f --- /dev/null +++ b/flutter/realm_flutter/example1/my_example13/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/flutter/realm_flutter/example1/my_example13/ios/Runner/Info.plist b/flutter/realm_flutter/example1/my_example13/ios/Runner/Info.plist new file mode 100644 index 000000000..7190b80c7 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example13/ios/Runner/Info.plist @@ -0,0 +1,45 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + my_example13 + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + + diff --git a/flutter/realm_flutter/example1/my_example13/ios/Runner/Runner-Bridging-Header.h b/flutter/realm_flutter/example1/my_example13/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 000000000..308a2a560 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example13/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/flutter/realm_flutter/example1/my_example13/lib/main.dart b/flutter/realm_flutter/example1/my_example13/lib/main.dart new file mode 100644 index 000000000..11655b668 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example13/lib/main.dart @@ -0,0 +1,117 @@ +import 'package:flutter/material.dart'; + +void main() { + runApp(MyApp()); +} + +class MyApp extends StatelessWidget { + // This widget is the root of your application. + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Flutter Demo', + theme: ThemeData( + // This is the theme of your application. + // + // Try running your application with "flutter run". You'll see the + // application has a blue toolbar. Then, without quitting the app, try + // changing the primarySwatch below to Colors.green and then invoke + // "hot reload" (press "r" in the console where you ran "flutter run", + // or simply save your changes to "hot reload" in a Flutter IDE). + // Notice that the counter didn't reset back to zero; the application + // is not restarted. + primarySwatch: Colors.blue, + // This makes the visual density adapt to the platform that you run + // the app on. For desktop platforms, the controls will be smaller and + // closer together (more dense) than on mobile platforms. + visualDensity: VisualDensity.adaptivePlatformDensity, + ), + home: MyHomePage(title: 'Flutter Demo Home Page'), + ); + } +} + +class MyHomePage extends StatefulWidget { + MyHomePage({Key key, this.title}) : super(key: key); + + // This widget is the home page of your application. It is stateful, meaning + // that it has a State object (defined below) that contains fields that affect + // how it looks. + + // This class is the configuration for the state. It holds the values (in this + // case the title) provided by the parent (in this case the App widget) and + // used by the build method of the State. Fields in a Widget subclass are + // always marked "final". + + final String title; + + @override + _MyHomePageState createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State { + int _counter = 0; + + void _incrementCounter() { + setState(() { + // This call to setState tells the Flutter framework that something has + // changed in this State, which causes it to rerun the build method below + // so that the display can reflect the updated values. If we changed + // _counter without calling setState(), then the build method would not be + // called again, and so nothing would appear to happen. + _counter++; + }); + } + + @override + Widget build(BuildContext context) { + // This method is rerun every time setState is called, for instance as done + // by the _incrementCounter method above. + // + // The Flutter framework has been optimized to make rerunning build methods + // fast, so that you can just rebuild anything that needs updating rather + // than having to individually change instances of widgets. + return Scaffold( + appBar: AppBar( + // Here we take the value from the MyHomePage object that was created by + // the App.build method, and use it to set our appbar title. + title: Text(widget.title), + ), + body: Center( + // Center is a layout widget. It takes a single child and positions it + // in the middle of the parent. + child: Column( + // Column is also a layout widget. It takes a list of children and + // arranges them vertically. By default, it sizes itself to fit its + // children horizontally, and tries to be as tall as its parent. + // + // Invoke "debug painting" (press "p" in the console, choose the + // "Toggle Debug Paint" action from the Flutter Inspector in Android + // Studio, or the "Toggle Debug Paint" command in Visual Studio Code) + // to see the wireframe for each widget. + // + // Column has various properties to control how it sizes itself and + // how it positions its children. Here we use mainAxisAlignment to + // center the children vertically; the main axis here is the vertical + // axis because Columns are vertical (the cross axis would be + // horizontal). + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'You have pushed the button this many times:', + ), + Text( + '$_counter', + style: Theme.of(context).textTheme.headline4, + ), + ], + ), + ), + floatingActionButton: FloatingActionButton( + onPressed: _incrementCounter, + tooltip: 'Increment', + child: Icon(Icons.add), + ), // This trailing comma makes auto-formatting nicer for build methods. + ); + } +} diff --git a/flutter/realm_flutter/example1/my_example13/pubspec.lock b/flutter/realm_flutter/example1/my_example13/pubspec.lock new file mode 100644 index 000000000..2cea8ddc1 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example13/pubspec.lock @@ -0,0 +1,483 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + url: "https://pub.dartlang.org" + source: hosted + version: "7.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + url: "https://pub.dartlang.org" + source: hosted + version: "0.39.17" + args: + dependency: transitive + description: + name: args + url: "https://pub.dartlang.org" + source: hosted + version: "1.6.0" + async: + dependency: transitive + description: + name: async + url: "https://pub.dartlang.org" + source: hosted + version: "2.5.0-nullsafety.1" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0-nullsafety.1" + build: + dependency: transitive + description: + name: build + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.0" + build_config: + dependency: transitive + description: + name: build_config + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.2" + build_daemon: + dependency: transitive + description: + name: build_daemon + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.4" + build_resolvers: + dependency: transitive + description: + name: build_resolvers + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.11" + build_runner: + dependency: transitive + description: + name: build_runner + url: "https://pub.dartlang.org" + source: hosted + version: "1.10.2" + build_runner_core: + dependency: transitive + description: + name: build_runner_core + url: "https://pub.dartlang.org" + source: hosted + version: "6.0.1" + built_collection: + dependency: transitive + description: + name: built_collection + url: "https://pub.dartlang.org" + source: hosted + version: "4.3.2" + built_value: + dependency: transitive + description: + name: built_value + url: "https://pub.dartlang.org" + source: hosted + version: "7.1.0" + characters: + dependency: transitive + description: + name: characters + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0-nullsafety.3" + charcode: + dependency: transitive + description: + name: charcode + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0-nullsafety.1" + checked_yaml: + dependency: transitive + description: + name: checked_yaml + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" + cli_util: + dependency: transitive + description: + name: cli_util + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.0" + clock: + dependency: transitive + description: + name: clock + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0-nullsafety.1" + code_builder: + dependency: transitive + description: + name: code_builder + url: "https://pub.dartlang.org" + source: hosted + version: "3.5.0" + collection: + dependency: transitive + description: + name: collection + url: "https://pub.dartlang.org" + source: hosted + version: "1.15.0-nullsafety.3" + convert: + dependency: transitive + description: + name: convert + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + crypto: + dependency: transitive + description: + name: crypto + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.5" + csslib: + dependency: transitive + description: + name: csslib + url: "https://pub.dartlang.org" + source: hosted + version: "0.16.2" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" + dart_style: + dependency: transitive + description: + name: dart_style + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.6" + fake_async: + dependency: transitive + description: + name: fake_async + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0-nullsafety.1" + fixnum: + dependency: transitive + description: + name: fixnum + url: "https://pub.dartlang.org" + source: hosted + version: "0.10.11" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + glob: + dependency: transitive + description: + name: glob + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + graphs: + dependency: transitive + description: + name: graphs + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.0" + html: + dependency: transitive + description: + name: html + url: "https://pub.dartlang.org" + source: hosted + version: "0.14.0+4" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.0" + http_parser: + dependency: transitive + description: + name: http_parser + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.4" + io: + dependency: transitive + description: + name: io + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.4" + js: + dependency: transitive + description: + name: js + url: "https://pub.dartlang.org" + source: hosted + version: "0.6.2" + json_annotation: + dependency: transitive + description: + name: json_annotation + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.0" + logging: + dependency: transitive + description: + name: logging + url: "https://pub.dartlang.org" + source: hosted + version: "0.11.4" + matcher: + dependency: transitive + description: + name: matcher + url: "https://pub.dartlang.org" + source: hosted + version: "0.12.10-nullsafety.1" + meta: + dependency: transitive + description: + name: meta + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.0-nullsafety.3" + mime: + dependency: transitive + description: + name: mime + url: "https://pub.dartlang.org" + source: hosted + version: "0.9.7" + node_interop: + dependency: transitive + description: + name: node_interop + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + node_io: + dependency: transitive + description: + name: node_io + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.1" + package_config: + dependency: transitive + description: + name: package_config + url: "https://pub.dartlang.org" + source: hosted + version: "1.9.3" + path: + dependency: transitive + description: + name: path + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.0-nullsafety.1" + pedantic: + dependency: transitive + description: + name: pedantic + url: "https://pub.dartlang.org" + source: hosted + version: "1.9.2" + pool: + dependency: transitive + description: + name: pool + url: "https://pub.dartlang.org" + source: hosted + version: "1.4.0" + pub_semver: + dependency: transitive + description: + name: pub_semver + url: "https://pub.dartlang.org" + source: hosted + version: "1.4.4" + pubspec_parse: + dependency: transitive + description: + name: pubspec_parse + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.5" + quiver: + dependency: transitive + description: + name: quiver + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.4+1" + realm_flutter: + dependency: "direct main" + description: + path: "../.." + relative: true + source: path + version: "0.0.1" + realm_generator: + dependency: transitive + description: + path: "../../../../generator" + relative: true + source: path + version: "1.0.0" + shelf: + dependency: transitive + description: + name: shelf + url: "https://pub.dartlang.org" + source: hosted + version: "0.7.9" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.3" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_gen: + dependency: transitive + description: + name: source_gen + url: "https://pub.dartlang.org" + source: hosted + version: "0.9.7+1" + source_span: + dependency: transitive + description: + name: source_span + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.0-nullsafety.2" + stack_trace: + dependency: transitive + description: + name: stack_trace + url: "https://pub.dartlang.org" + source: hosted + version: "1.10.0-nullsafety.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0-nullsafety.1" + stream_transform: + dependency: transitive + description: + name: stream_transform + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + string_scanner: + dependency: transitive + description: + name: string_scanner + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0-nullsafety.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0-nullsafety.1" + test_api: + dependency: transitive + description: + name: test_api + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.19-nullsafety.2" + timing: + dependency: transitive + description: + name: timing + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.1+2" + typed_data: + dependency: transitive + description: + name: typed_data + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.0-nullsafety.3" + vector_math: + dependency: transitive + description: + name: vector_math + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0-nullsafety.3" + watcher: + dependency: transitive + description: + name: watcher + url: "https://pub.dartlang.org" + source: hosted + version: "0.9.7+15" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + yaml: + dependency: transitive + description: + name: yaml + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.1" +sdks: + dart: ">=2.10.0-110 <2.11.0" + flutter: ">=1.10.0" diff --git a/flutter/realm_flutter/example1/my_example13/pubspec.yaml b/flutter/realm_flutter/example1/my_example13/pubspec.yaml new file mode 100644 index 000000000..06757592d --- /dev/null +++ b/flutter/realm_flutter/example1/my_example13/pubspec.yaml @@ -0,0 +1,83 @@ +name: my_example13 +description: A new Flutter project. + +# The following line prevents the package from being accidentally published to +# pub.dev using `pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +# The following defines the version and build number for your application. +# A version number is three numbers separated by dots, like 1.2.43 +# followed by an optional build number separated by a +. +# Both the version and the builder number may be overridden in flutter +# build by specifying --build-name and --build-number, respectively. +# In Android, build-name is used as versionName while build-number used as versionCode. +# Read more about Android versioning at https://developer.android.com/studio/publish/versioning +# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. +# Read more about iOS versioning at +# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html +version: 1.0.0+1 + +environment: + sdk: ">=2.7.0 <3.0.0" + +dependencies: + flutter: + sdk: flutter + realm_flutter: + # When depending on this package from a real application you should use: + # realm_flutter: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # The example app is bundled with the plugin so we use a path dependency on + # the parent directory to use the current plugin's version. + path: ../../ + + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.0 + +dev_dependencies: + flutter_test: + sdk: flutter + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware. + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/custom-fonts/#from-packages diff --git a/flutter/realm_flutter/example1/my_example13/test/widget_test.dart b/flutter/realm_flutter/example1/my_example13/test/widget_test.dart new file mode 100644 index 000000000..a32f5e62c --- /dev/null +++ b/flutter/realm_flutter/example1/my_example13/test/widget_test.dart @@ -0,0 +1,30 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility that Flutter provides. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:my_example13/main.dart'; + +void main() { + testWidgets('Counter increments smoke test', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(MyApp()); + + // Verify that our counter starts at 0. + expect(find.text('0'), findsOneWidget); + expect(find.text('1'), findsNothing); + + // Tap the '+' icon and trigger a frame. + await tester.tap(find.byIcon(Icons.add)); + await tester.pump(); + + // Verify that our counter has incremented. + expect(find.text('0'), findsNothing); + expect(find.text('1'), findsOneWidget); + }); +} diff --git a/flutter/realm_flutter/example1/my_example3/.gitignore b/flutter/realm_flutter/example1/my_example3/.gitignore new file mode 100644 index 000000000..9d532b18a --- /dev/null +++ b/flutter/realm_flutter/example1/my_example3/.gitignore @@ -0,0 +1,41 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Web related +lib/generated_plugin_registrant.dart + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json diff --git a/flutter/realm_flutter/example1/my_example3/.metadata b/flutter/realm_flutter/example1/my_example3/.metadata new file mode 100644 index 000000000..24544cb74 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example3/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 84f3d28555368a70270e9ac8390a9441df95e752 + channel: stable + +project_type: app diff --git a/flutter/realm_flutter/example1/my_example3/README.md b/flutter/realm_flutter/example1/my_example3/README.md new file mode 100644 index 000000000..675189314 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example3/README.md @@ -0,0 +1,16 @@ +# my_example3 + +A new Flutter project. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) + +For help getting started with Flutter, view our +[online documentation](https://flutter.dev/docs), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/flutter/realm_flutter/example1/my_example3/android/.gitignore b/flutter/realm_flutter/example1/my_example3/android/.gitignore new file mode 100644 index 000000000..0a741cb43 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example3/android/.gitignore @@ -0,0 +1,11 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +key.properties diff --git a/flutter/realm_flutter/example1/my_example3/android/app/build.gradle b/flutter/realm_flutter/example1/my_example3/android/app/build.gradle new file mode 100644 index 000000000..93d115415 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example3/android/app/build.gradle @@ -0,0 +1,63 @@ +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterRoot = localProperties.getProperty('flutter.sdk') +if (flutterRoot == null) { + throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + +android { + compileSdkVersion 29 + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } + + lintOptions { + disable 'InvalidPackage' + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId "com.example.my_example3" + minSdkVersion 16 + targetSdkVersion 29 + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig signingConfigs.debug + } + } +} + +flutter { + source '../..' +} + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" +} diff --git a/flutter/realm_flutter/example1/my_example3/android/app/src/debug/AndroidManifest.xml b/flutter/realm_flutter/example1/my_example3/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 000000000..1915f11cb --- /dev/null +++ b/flutter/realm_flutter/example1/my_example3/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/flutter/realm_flutter/example1/my_example3/android/app/src/main/AndroidManifest.xml b/flutter/realm_flutter/example1/my_example3/android/app/src/main/AndroidManifest.xml new file mode 100644 index 000000000..dab559e71 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example3/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + diff --git a/flutter/realm_flutter/example1/my_example3/android/app/src/main/kotlin/com/example/my_example3/MainActivity.kt b/flutter/realm_flutter/example1/my_example3/android/app/src/main/kotlin/com/example/my_example3/MainActivity.kt new file mode 100644 index 000000000..e99b55422 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example3/android/app/src/main/kotlin/com/example/my_example3/MainActivity.kt @@ -0,0 +1,6 @@ +package com.example.my_example3 + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() { +} diff --git a/flutter/realm_flutter/example1/my_example3/android/app/src/main/res/drawable/launch_background.xml b/flutter/realm_flutter/example1/my_example3/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 000000000..304732f88 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example3/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/flutter/realm_flutter/example1/my_example3/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/flutter/realm_flutter/example1/my_example3/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..db77bb4b7 Binary files /dev/null and b/flutter/realm_flutter/example1/my_example3/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/flutter/realm_flutter/example1/my_example3/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/flutter/realm_flutter/example1/my_example3/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..17987b79b Binary files /dev/null and b/flutter/realm_flutter/example1/my_example3/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/flutter/realm_flutter/example1/my_example3/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/flutter/realm_flutter/example1/my_example3/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..09d439148 Binary files /dev/null and b/flutter/realm_flutter/example1/my_example3/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/flutter/realm_flutter/example1/my_example3/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/flutter/realm_flutter/example1/my_example3/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..d5f1c8d34 Binary files /dev/null and b/flutter/realm_flutter/example1/my_example3/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/flutter/realm_flutter/example1/my_example3/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/flutter/realm_flutter/example1/my_example3/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..4d6372eeb Binary files /dev/null and b/flutter/realm_flutter/example1/my_example3/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/flutter/realm_flutter/example1/my_example3/android/app/src/main/res/values/styles.xml b/flutter/realm_flutter/example1/my_example3/android/app/src/main/res/values/styles.xml new file mode 100644 index 000000000..1f83a33fd --- /dev/null +++ b/flutter/realm_flutter/example1/my_example3/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/flutter/realm_flutter/example1/my_example3/android/app/src/profile/AndroidManifest.xml b/flutter/realm_flutter/example1/my_example3/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 000000000..1915f11cb --- /dev/null +++ b/flutter/realm_flutter/example1/my_example3/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/flutter/realm_flutter/example1/my_example3/android/build.gradle b/flutter/realm_flutter/example1/my_example3/android/build.gradle new file mode 100644 index 000000000..3100ad2d5 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example3/android/build.gradle @@ -0,0 +1,31 @@ +buildscript { + ext.kotlin_version = '1.3.50' + repositories { + google() + jcenter() + } + + dependencies { + classpath 'com.android.tools.build:gradle:3.5.0' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +allprojects { + repositories { + google() + jcenter() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/flutter/realm_flutter/example1/my_example3/android/gradle.properties b/flutter/realm_flutter/example1/my_example3/android/gradle.properties new file mode 100644 index 000000000..94adc3a3f --- /dev/null +++ b/flutter/realm_flutter/example1/my_example3/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx1536M +android.useAndroidX=true +android.enableJetifier=true diff --git a/flutter/realm_flutter/example1/my_example3/android/gradle/wrapper/gradle-wrapper.properties b/flutter/realm_flutter/example1/my_example3/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..296b146b7 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example3/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Fri Jun 23 08:50:38 CEST 2017 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip diff --git a/flutter/realm_flutter/example1/my_example3/android/settings.gradle b/flutter/realm_flutter/example1/my_example3/android/settings.gradle new file mode 100644 index 000000000..44e62bcf0 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example3/android/settings.gradle @@ -0,0 +1,11 @@ +include ':app' + +def localPropertiesFile = new File(rootProject.projectDir, "local.properties") +def properties = new Properties() + +assert localPropertiesFile.exists() +localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } + +def flutterSdkPath = properties.getProperty("flutter.sdk") +assert flutterSdkPath != null, "flutter.sdk not set in local.properties" +apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/flutter/realm_flutter/example1/my_example3/ios/.gitignore b/flutter/realm_flutter/example1/my_example3/ios/.gitignore new file mode 100644 index 000000000..e96ef602b --- /dev/null +++ b/flutter/realm_flutter/example1/my_example3/ios/.gitignore @@ -0,0 +1,32 @@ +*.mode1v3 +*.mode2v3 +*.moved-aside +*.pbxuser +*.perspectivev3 +**/*sync/ +.sconsign.dblite +.tags* +**/.vagrant/ +**/DerivedData/ +Icon? +**/Pods/ +**/.symlinks/ +profile +xcuserdata +**/.generated/ +Flutter/App.framework +Flutter/Flutter.framework +Flutter/Flutter.podspec +Flutter/Generated.xcconfig +Flutter/app.flx +Flutter/app.zip +Flutter/flutter_assets/ +Flutter/flutter_export_environment.sh +ServiceDefinitions.json +Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!default.mode1v3 +!default.mode2v3 +!default.pbxuser +!default.perspectivev3 diff --git a/flutter/realm_flutter/example1/my_example3/ios/Flutter/AppFrameworkInfo.plist b/flutter/realm_flutter/example1/my_example3/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 000000000..f2872cf47 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example3/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + MinimumOSVersion + 9.0 + + diff --git a/flutter/realm_flutter/example1/my_example3/ios/Flutter/Debug.xcconfig b/flutter/realm_flutter/example1/my_example3/ios/Flutter/Debug.xcconfig new file mode 100644 index 000000000..e8efba114 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example3/ios/Flutter/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "Generated.xcconfig" diff --git a/flutter/realm_flutter/example1/my_example3/ios/Flutter/Release.xcconfig b/flutter/realm_flutter/example1/my_example3/ios/Flutter/Release.xcconfig new file mode 100644 index 000000000..399e9340e --- /dev/null +++ b/flutter/realm_flutter/example1/my_example3/ios/Flutter/Release.xcconfig @@ -0,0 +1,2 @@ +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "Generated.xcconfig" diff --git a/flutter/realm_flutter/example1/my_example3/ios/Podfile b/flutter/realm_flutter/example1/my_example3/ios/Podfile new file mode 100644 index 000000000..1e8c3c90a --- /dev/null +++ b/flutter/realm_flutter/example1/my_example3/ios/Podfile @@ -0,0 +1,41 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '9.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + end +end diff --git a/flutter/realm_flutter/example1/my_example3/ios/Podfile.lock b/flutter/realm_flutter/example1/my_example3/ios/Podfile.lock new file mode 100644 index 000000000..5a04e517d --- /dev/null +++ b/flutter/realm_flutter/example1/my_example3/ios/Podfile.lock @@ -0,0 +1,22 @@ +PODS: + - Flutter (1.0.0) + - realm_flutter (0.0.1): + - Flutter + +DEPENDENCIES: + - Flutter (from `Flutter`) + - realm_flutter (from `.symlinks/plugins/realm_flutter/ios`) + +EXTERNAL SOURCES: + Flutter: + :path: Flutter + realm_flutter: + :path: ".symlinks/plugins/realm_flutter/ios" + +SPEC CHECKSUMS: + Flutter: 0e3d915762c693b495b44d77113d4970485de6ec + realm_flutter: 5cef5e47913216036a5800c606781b5de0ba8b64 + +PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c + +COCOAPODS: 1.8.4 diff --git a/flutter/realm_flutter/example1/my_example3/ios/Runner.xcodeproj/project.pbxproj b/flutter/realm_flutter/example1/my_example3/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 000000000..878f49ea9 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example3/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,566 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + DA559009DA248447E704DAC7 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 57ED742176CD1C20A70FD244 /* Pods_Runner.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 29E3B53AB0D593508BE48CF0 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 57ED742176CD1C20A70FD244 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + C1DD29AA9A514DF76DD6F0C3 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + DEC2EC51F1E1BCD59E005BFB /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + DA559009DA248447E704DAC7 /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 777DEF677A4B5ED37F26341E /* Frameworks */ = { + isa = PBXGroup; + children = ( + 57ED742176CD1C20A70FD244 /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + 9B052108E55814FBF59FD82A /* Pods */, + 777DEF677A4B5ED37F26341E /* Frameworks */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, + ); + path = Runner; + sourceTree = ""; + }; + 9B052108E55814FBF59FD82A /* Pods */ = { + isa = PBXGroup; + children = ( + DEC2EC51F1E1BCD59E005BFB /* Pods-Runner.debug.xcconfig */, + 29E3B53AB0D593508BE48CF0 /* Pods-Runner.release.xcconfig */, + C1DD29AA9A514DF76DD6F0C3 /* Pods-Runner.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 11DB2FBA115CF7A32BF06C6F /* [CP] Check Pods Manifest.lock */, + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + 586FCB0F593AC10D15F0360F /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1020; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 11DB2FBA115CF7A32BF06C6F /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-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; + }; + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$PROJECT_DIR/.symlinks/plugins/realm_flutter/ios/scripts/xcode_backend.sh\" embed_and_thin"; + }; + 586FCB0F593AC10D15F0360F /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", + "${PODS_ROOT}/../Flutter/Flutter.framework", + "${BUILT_PRODUCTS_DIR}/realm_flutter/realm_flutter.framework", + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/realm_flutter.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$PROJECT_DIR/.symlinks/plugins/realm_flutter/ios/scripts/xcode_backend.sh\" build"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + 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_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + 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_IMPLICIT_RETAIN_SELF = 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_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "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 = gnu99; + 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 = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/.symlinks/plugins/realm_flutter/ios/Frameworks/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/.symlinks/plugins/realm_flutter/ios/Frameworks/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.myExample3; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + 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_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + 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_IMPLICIT_RETAIN_SELF = 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_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + 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 = 9.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + 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_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + 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_IMPLICIT_RETAIN_SELF = 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_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "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 = gnu99; + 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 = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/.symlinks/plugins/realm_flutter/ios/Frameworks/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/.symlinks/plugins/realm_flutter/ios/Frameworks/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.myExample3; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/.symlinks/plugins/realm_flutter/ios/Frameworks/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/.symlinks/plugins/realm_flutter/ios/Frameworks/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.myExample3; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/flutter/realm_flutter/example1/my_example3/ios/Runner.xcodeproj/project.pbxproj.bak b/flutter/realm_flutter/example1/my_example3/ios/Runner.xcodeproj/project.pbxproj.bak new file mode 100644 index 000000000..878f49ea9 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example3/ios/Runner.xcodeproj/project.pbxproj.bak @@ -0,0 +1,566 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + DA559009DA248447E704DAC7 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 57ED742176CD1C20A70FD244 /* Pods_Runner.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 29E3B53AB0D593508BE48CF0 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 57ED742176CD1C20A70FD244 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + C1DD29AA9A514DF76DD6F0C3 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + DEC2EC51F1E1BCD59E005BFB /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + DA559009DA248447E704DAC7 /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 777DEF677A4B5ED37F26341E /* Frameworks */ = { + isa = PBXGroup; + children = ( + 57ED742176CD1C20A70FD244 /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + 9B052108E55814FBF59FD82A /* Pods */, + 777DEF677A4B5ED37F26341E /* Frameworks */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, + ); + path = Runner; + sourceTree = ""; + }; + 9B052108E55814FBF59FD82A /* Pods */ = { + isa = PBXGroup; + children = ( + DEC2EC51F1E1BCD59E005BFB /* Pods-Runner.debug.xcconfig */, + 29E3B53AB0D593508BE48CF0 /* Pods-Runner.release.xcconfig */, + C1DD29AA9A514DF76DD6F0C3 /* Pods-Runner.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 11DB2FBA115CF7A32BF06C6F /* [CP] Check Pods Manifest.lock */, + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + 586FCB0F593AC10D15F0360F /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1020; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 11DB2FBA115CF7A32BF06C6F /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-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; + }; + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$PROJECT_DIR/.symlinks/plugins/realm_flutter/ios/scripts/xcode_backend.sh\" embed_and_thin"; + }; + 586FCB0F593AC10D15F0360F /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", + "${PODS_ROOT}/../Flutter/Flutter.framework", + "${BUILT_PRODUCTS_DIR}/realm_flutter/realm_flutter.framework", + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/realm_flutter.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$PROJECT_DIR/.symlinks/plugins/realm_flutter/ios/scripts/xcode_backend.sh\" build"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + 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_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + 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_IMPLICIT_RETAIN_SELF = 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_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "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 = gnu99; + 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 = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/.symlinks/plugins/realm_flutter/ios/Frameworks/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/.symlinks/plugins/realm_flutter/ios/Frameworks/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.myExample3; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + 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_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + 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_IMPLICIT_RETAIN_SELF = 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_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + 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 = 9.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + 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_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + 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_IMPLICIT_RETAIN_SELF = 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_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "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 = gnu99; + 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 = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/.symlinks/plugins/realm_flutter/ios/Frameworks/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/.symlinks/plugins/realm_flutter/ios/Frameworks/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.myExample3; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/.symlinks/plugins/realm_flutter/ios/Frameworks/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/.symlinks/plugins/realm_flutter/ios/Frameworks/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.myExample3; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/flutter/realm_flutter/example1/my_example3/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/flutter/realm_flutter/example1/my_example3/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..1d526a16e --- /dev/null +++ b/flutter/realm_flutter/example1/my_example3/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/flutter/realm_flutter/example1/my_example3/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/flutter/realm_flutter/example1/my_example3/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example3/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/flutter/realm_flutter/example1/my_example3/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/flutter/realm_flutter/example1/my_example3/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 000000000..f9b0d7c5e --- /dev/null +++ b/flutter/realm_flutter/example1/my_example3/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/flutter/realm_flutter/example1/my_example3/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/flutter/realm_flutter/example1/my_example3/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 000000000..a28140cfd --- /dev/null +++ b/flutter/realm_flutter/example1/my_example3/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/flutter/realm_flutter/example1/my_example3/ios/Runner.xcworkspace/contents.xcworkspacedata b/flutter/realm_flutter/example1/my_example3/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..21a3cc14c --- /dev/null +++ b/flutter/realm_flutter/example1/my_example3/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/flutter/realm_flutter/example1/my_example3/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/flutter/realm_flutter/example1/my_example3/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example3/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/flutter/realm_flutter/example1/my_example3/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/flutter/realm_flutter/example1/my_example3/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 000000000..f9b0d7c5e --- /dev/null +++ b/flutter/realm_flutter/example1/my_example3/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/flutter/realm_flutter/example1/my_example3/ios/Runner/AppDelegate.swift b/flutter/realm_flutter/example1/my_example3/ios/Runner/AppDelegate.swift new file mode 100644 index 000000000..70693e4a8 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example3/ios/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import UIKit +import Flutter + +@UIApplicationMain +@objc class AppDelegate: FlutterAppDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + GeneratedPluginRegistrant.register(with: self) + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } +} diff --git a/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000..d36b1fab2 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,122 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "Icon-App-1024x1024@1x.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 000000000..dc9ada472 Binary files /dev/null and b/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ diff --git a/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100644 index 000000000..28c6bf030 Binary files /dev/null and b/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ diff --git a/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 000000000..2ccbfd967 Binary files /dev/null and b/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ diff --git a/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 000000000..f091b6b0b Binary files /dev/null and b/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ diff --git a/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 000000000..4cde12118 Binary files /dev/null and b/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ diff --git a/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 000000000..d0ef06e7e Binary files /dev/null and b/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ diff --git a/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png new file mode 100644 index 000000000..dcdc2306c Binary files /dev/null and b/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ diff --git a/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png new file mode 100644 index 000000000..2ccbfd967 Binary files /dev/null and b/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ diff --git a/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 000000000..c8f9ed8f5 Binary files /dev/null and b/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ diff --git a/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 000000000..a6d6b8609 Binary files /dev/null and b/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ diff --git a/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 000000000..a6d6b8609 Binary files /dev/null and b/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ diff --git a/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 000000000..75b2d164a Binary files /dev/null and b/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ diff --git a/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 000000000..c4df70d39 Binary files /dev/null and b/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ diff --git a/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png new file mode 100644 index 000000000..6a84f41e1 Binary files /dev/null and b/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ diff --git a/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 000000000..d0e1f5853 Binary files /dev/null and b/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ diff --git a/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json new file mode 100644 index 000000000..0bedcf2fd --- /dev/null +++ b/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "LaunchImage.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png new file mode 100644 index 000000000..9da19eaca Binary files /dev/null and b/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ diff --git a/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 000000000..9da19eaca Binary files /dev/null and b/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ diff --git a/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 000000000..9da19eaca Binary files /dev/null and b/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ diff --git a/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 000000000..89c2725b7 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example3/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -0,0 +1,5 @@ +# Launch Screen Assets + +You can customize the launch screen with your own desired assets by replacing the image files in this directory. + +You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/flutter/realm_flutter/example1/my_example3/ios/Runner/Base.lproj/LaunchScreen.storyboard b/flutter/realm_flutter/example1/my_example3/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 000000000..f2e259c7c --- /dev/null +++ b/flutter/realm_flutter/example1/my_example3/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/flutter/realm_flutter/example1/my_example3/ios/Runner/Base.lproj/Main.storyboard b/flutter/realm_flutter/example1/my_example3/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 000000000..f3c28516f --- /dev/null +++ b/flutter/realm_flutter/example1/my_example3/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/flutter/realm_flutter/example1/my_example3/ios/Runner/Info.plist b/flutter/realm_flutter/example1/my_example3/ios/Runner/Info.plist new file mode 100644 index 000000000..ce6ea5a7e --- /dev/null +++ b/flutter/realm_flutter/example1/my_example3/ios/Runner/Info.plist @@ -0,0 +1,45 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + my_example3 + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + + diff --git a/flutter/realm_flutter/example1/my_example3/ios/Runner/Runner-Bridging-Header.h b/flutter/realm_flutter/example1/my_example3/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 000000000..308a2a560 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example3/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/flutter/realm_flutter/example1/my_example3/lib/main.dart b/flutter/realm_flutter/example1/my_example3/lib/main.dart new file mode 100644 index 000000000..11655b668 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example3/lib/main.dart @@ -0,0 +1,117 @@ +import 'package:flutter/material.dart'; + +void main() { + runApp(MyApp()); +} + +class MyApp extends StatelessWidget { + // This widget is the root of your application. + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Flutter Demo', + theme: ThemeData( + // This is the theme of your application. + // + // Try running your application with "flutter run". You'll see the + // application has a blue toolbar. Then, without quitting the app, try + // changing the primarySwatch below to Colors.green and then invoke + // "hot reload" (press "r" in the console where you ran "flutter run", + // or simply save your changes to "hot reload" in a Flutter IDE). + // Notice that the counter didn't reset back to zero; the application + // is not restarted. + primarySwatch: Colors.blue, + // This makes the visual density adapt to the platform that you run + // the app on. For desktop platforms, the controls will be smaller and + // closer together (more dense) than on mobile platforms. + visualDensity: VisualDensity.adaptivePlatformDensity, + ), + home: MyHomePage(title: 'Flutter Demo Home Page'), + ); + } +} + +class MyHomePage extends StatefulWidget { + MyHomePage({Key key, this.title}) : super(key: key); + + // This widget is the home page of your application. It is stateful, meaning + // that it has a State object (defined below) that contains fields that affect + // how it looks. + + // This class is the configuration for the state. It holds the values (in this + // case the title) provided by the parent (in this case the App widget) and + // used by the build method of the State. Fields in a Widget subclass are + // always marked "final". + + final String title; + + @override + _MyHomePageState createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State { + int _counter = 0; + + void _incrementCounter() { + setState(() { + // This call to setState tells the Flutter framework that something has + // changed in this State, which causes it to rerun the build method below + // so that the display can reflect the updated values. If we changed + // _counter without calling setState(), then the build method would not be + // called again, and so nothing would appear to happen. + _counter++; + }); + } + + @override + Widget build(BuildContext context) { + // This method is rerun every time setState is called, for instance as done + // by the _incrementCounter method above. + // + // The Flutter framework has been optimized to make rerunning build methods + // fast, so that you can just rebuild anything that needs updating rather + // than having to individually change instances of widgets. + return Scaffold( + appBar: AppBar( + // Here we take the value from the MyHomePage object that was created by + // the App.build method, and use it to set our appbar title. + title: Text(widget.title), + ), + body: Center( + // Center is a layout widget. It takes a single child and positions it + // in the middle of the parent. + child: Column( + // Column is also a layout widget. It takes a list of children and + // arranges them vertically. By default, it sizes itself to fit its + // children horizontally, and tries to be as tall as its parent. + // + // Invoke "debug painting" (press "p" in the console, choose the + // "Toggle Debug Paint" action from the Flutter Inspector in Android + // Studio, or the "Toggle Debug Paint" command in Visual Studio Code) + // to see the wireframe for each widget. + // + // Column has various properties to control how it sizes itself and + // how it positions its children. Here we use mainAxisAlignment to + // center the children vertically; the main axis here is the vertical + // axis because Columns are vertical (the cross axis would be + // horizontal). + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'You have pushed the button this many times:', + ), + Text( + '$_counter', + style: Theme.of(context).textTheme.headline4, + ), + ], + ), + ), + floatingActionButton: FloatingActionButton( + onPressed: _incrementCounter, + tooltip: 'Increment', + child: Icon(Icons.add), + ), // This trailing comma makes auto-formatting nicer for build methods. + ); + } +} diff --git a/flutter/realm_flutter/example1/my_example3/pubspec.lock b/flutter/realm_flutter/example1/my_example3/pubspec.lock new file mode 100644 index 000000000..2cea8ddc1 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example3/pubspec.lock @@ -0,0 +1,483 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + url: "https://pub.dartlang.org" + source: hosted + version: "7.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + url: "https://pub.dartlang.org" + source: hosted + version: "0.39.17" + args: + dependency: transitive + description: + name: args + url: "https://pub.dartlang.org" + source: hosted + version: "1.6.0" + async: + dependency: transitive + description: + name: async + url: "https://pub.dartlang.org" + source: hosted + version: "2.5.0-nullsafety.1" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0-nullsafety.1" + build: + dependency: transitive + description: + name: build + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.0" + build_config: + dependency: transitive + description: + name: build_config + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.2" + build_daemon: + dependency: transitive + description: + name: build_daemon + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.4" + build_resolvers: + dependency: transitive + description: + name: build_resolvers + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.11" + build_runner: + dependency: transitive + description: + name: build_runner + url: "https://pub.dartlang.org" + source: hosted + version: "1.10.2" + build_runner_core: + dependency: transitive + description: + name: build_runner_core + url: "https://pub.dartlang.org" + source: hosted + version: "6.0.1" + built_collection: + dependency: transitive + description: + name: built_collection + url: "https://pub.dartlang.org" + source: hosted + version: "4.3.2" + built_value: + dependency: transitive + description: + name: built_value + url: "https://pub.dartlang.org" + source: hosted + version: "7.1.0" + characters: + dependency: transitive + description: + name: characters + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0-nullsafety.3" + charcode: + dependency: transitive + description: + name: charcode + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0-nullsafety.1" + checked_yaml: + dependency: transitive + description: + name: checked_yaml + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" + cli_util: + dependency: transitive + description: + name: cli_util + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.0" + clock: + dependency: transitive + description: + name: clock + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0-nullsafety.1" + code_builder: + dependency: transitive + description: + name: code_builder + url: "https://pub.dartlang.org" + source: hosted + version: "3.5.0" + collection: + dependency: transitive + description: + name: collection + url: "https://pub.dartlang.org" + source: hosted + version: "1.15.0-nullsafety.3" + convert: + dependency: transitive + description: + name: convert + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + crypto: + dependency: transitive + description: + name: crypto + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.5" + csslib: + dependency: transitive + description: + name: csslib + url: "https://pub.dartlang.org" + source: hosted + version: "0.16.2" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" + dart_style: + dependency: transitive + description: + name: dart_style + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.6" + fake_async: + dependency: transitive + description: + name: fake_async + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0-nullsafety.1" + fixnum: + dependency: transitive + description: + name: fixnum + url: "https://pub.dartlang.org" + source: hosted + version: "0.10.11" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + glob: + dependency: transitive + description: + name: glob + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + graphs: + dependency: transitive + description: + name: graphs + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.0" + html: + dependency: transitive + description: + name: html + url: "https://pub.dartlang.org" + source: hosted + version: "0.14.0+4" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.0" + http_parser: + dependency: transitive + description: + name: http_parser + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.4" + io: + dependency: transitive + description: + name: io + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.4" + js: + dependency: transitive + description: + name: js + url: "https://pub.dartlang.org" + source: hosted + version: "0.6.2" + json_annotation: + dependency: transitive + description: + name: json_annotation + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.0" + logging: + dependency: transitive + description: + name: logging + url: "https://pub.dartlang.org" + source: hosted + version: "0.11.4" + matcher: + dependency: transitive + description: + name: matcher + url: "https://pub.dartlang.org" + source: hosted + version: "0.12.10-nullsafety.1" + meta: + dependency: transitive + description: + name: meta + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.0-nullsafety.3" + mime: + dependency: transitive + description: + name: mime + url: "https://pub.dartlang.org" + source: hosted + version: "0.9.7" + node_interop: + dependency: transitive + description: + name: node_interop + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + node_io: + dependency: transitive + description: + name: node_io + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.1" + package_config: + dependency: transitive + description: + name: package_config + url: "https://pub.dartlang.org" + source: hosted + version: "1.9.3" + path: + dependency: transitive + description: + name: path + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.0-nullsafety.1" + pedantic: + dependency: transitive + description: + name: pedantic + url: "https://pub.dartlang.org" + source: hosted + version: "1.9.2" + pool: + dependency: transitive + description: + name: pool + url: "https://pub.dartlang.org" + source: hosted + version: "1.4.0" + pub_semver: + dependency: transitive + description: + name: pub_semver + url: "https://pub.dartlang.org" + source: hosted + version: "1.4.4" + pubspec_parse: + dependency: transitive + description: + name: pubspec_parse + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.5" + quiver: + dependency: transitive + description: + name: quiver + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.4+1" + realm_flutter: + dependency: "direct main" + description: + path: "../.." + relative: true + source: path + version: "0.0.1" + realm_generator: + dependency: transitive + description: + path: "../../../../generator" + relative: true + source: path + version: "1.0.0" + shelf: + dependency: transitive + description: + name: shelf + url: "https://pub.dartlang.org" + source: hosted + version: "0.7.9" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.3" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_gen: + dependency: transitive + description: + name: source_gen + url: "https://pub.dartlang.org" + source: hosted + version: "0.9.7+1" + source_span: + dependency: transitive + description: + name: source_span + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.0-nullsafety.2" + stack_trace: + dependency: transitive + description: + name: stack_trace + url: "https://pub.dartlang.org" + source: hosted + version: "1.10.0-nullsafety.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0-nullsafety.1" + stream_transform: + dependency: transitive + description: + name: stream_transform + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + string_scanner: + dependency: transitive + description: + name: string_scanner + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0-nullsafety.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0-nullsafety.1" + test_api: + dependency: transitive + description: + name: test_api + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.19-nullsafety.2" + timing: + dependency: transitive + description: + name: timing + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.1+2" + typed_data: + dependency: transitive + description: + name: typed_data + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.0-nullsafety.3" + vector_math: + dependency: transitive + description: + name: vector_math + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0-nullsafety.3" + watcher: + dependency: transitive + description: + name: watcher + url: "https://pub.dartlang.org" + source: hosted + version: "0.9.7+15" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + yaml: + dependency: transitive + description: + name: yaml + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.1" +sdks: + dart: ">=2.10.0-110 <2.11.0" + flutter: ">=1.10.0" diff --git a/flutter/realm_flutter/example1/my_example3/pubspec.yaml b/flutter/realm_flutter/example1/my_example3/pubspec.yaml new file mode 100644 index 000000000..59a715459 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example3/pubspec.yaml @@ -0,0 +1,83 @@ +name: my_example3 +description: A new Flutter project. + +# The following line prevents the package from being accidentally published to +# pub.dev using `pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +# The following defines the version and build number for your application. +# A version number is three numbers separated by dots, like 1.2.43 +# followed by an optional build number separated by a +. +# Both the version and the builder number may be overridden in flutter +# build by specifying --build-name and --build-number, respectively. +# In Android, build-name is used as versionName while build-number used as versionCode. +# Read more about Android versioning at https://developer.android.com/studio/publish/versioning +# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. +# Read more about iOS versioning at +# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html +version: 1.0.0+1 + +environment: + sdk: ">=2.7.0 <3.0.0" + +dependencies: + flutter: + sdk: flutter + + realm_flutter: + # When depending on this package from a real application you should use: + # realm_flutter: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # The example app is bundled with the plugin so we use a path dependency on + # the parent directory to use the current plugin's version. + path: ../../ + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.0 + +dev_dependencies: + flutter_test: + sdk: flutter + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware. + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/custom-fonts/#from-packages diff --git a/flutter/realm_flutter/example1/my_example3/test/widget_test.dart b/flutter/realm_flutter/example1/my_example3/test/widget_test.dart new file mode 100644 index 000000000..1abea796e --- /dev/null +++ b/flutter/realm_flutter/example1/my_example3/test/widget_test.dart @@ -0,0 +1,30 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility that Flutter provides. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:my_example3/main.dart'; + +void main() { + testWidgets('Counter increments smoke test', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(MyApp()); + + // Verify that our counter starts at 0. + expect(find.text('0'), findsOneWidget); + expect(find.text('1'), findsNothing); + + // Tap the '+' icon and trigger a frame. + await tester.tap(find.byIcon(Icons.add)); + await tester.pump(); + + // Verify that our counter has incremented. + expect(find.text('0'), findsNothing); + expect(find.text('1'), findsOneWidget); + }); +} diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/.gitignore b/flutter/realm_flutter/example1/my_example_test_dart_native/.gitignore new file mode 100644 index 000000000..9d532b18a --- /dev/null +++ b/flutter/realm_flutter/example1/my_example_test_dart_native/.gitignore @@ -0,0 +1,41 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Web related +lib/generated_plugin_registrant.dart + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/.metadata b/flutter/realm_flutter/example1/my_example_test_dart_native/.metadata new file mode 100644 index 000000000..24544cb74 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example_test_dart_native/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 84f3d28555368a70270e9ac8390a9441df95e752 + channel: stable + +project_type: app diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/README.md b/flutter/realm_flutter/example1/my_example_test_dart_native/README.md new file mode 100644 index 000000000..56e00f980 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example_test_dart_native/README.md @@ -0,0 +1,16 @@ +# my_example_test_dart_native + +A new Flutter project. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) + +For help getting started with Flutter, view our +[online documentation](https://flutter.dev/docs), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/android/.gitignore b/flutter/realm_flutter/example1/my_example_test_dart_native/android/.gitignore new file mode 100644 index 000000000..0a741cb43 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example_test_dart_native/android/.gitignore @@ -0,0 +1,11 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +key.properties diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/android/app/build.gradle b/flutter/realm_flutter/example1/my_example_test_dart_native/android/app/build.gradle new file mode 100644 index 000000000..c2ff5034e --- /dev/null +++ b/flutter/realm_flutter/example1/my_example_test_dart_native/android/app/build.gradle @@ -0,0 +1,63 @@ +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterRoot = localProperties.getProperty('flutter.sdk') +if (flutterRoot == null) { + throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + +android { + compileSdkVersion 29 + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } + + lintOptions { + disable 'InvalidPackage' + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId "com.example.my_example_test_dart_native" + minSdkVersion 16 + targetSdkVersion 29 + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig signingConfigs.debug + } + } +} + +flutter { + source '../..' +} + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" +} diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/android/app/src/debug/AndroidManifest.xml b/flutter/realm_flutter/example1/my_example_test_dart_native/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 000000000..ffc57aa59 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example_test_dart_native/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/android/app/src/main/AndroidManifest.xml b/flutter/realm_flutter/example1/my_example_test_dart_native/android/app/src/main/AndroidManifest.xml new file mode 100644 index 000000000..7de26c407 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example_test_dart_native/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/android/app/src/main/kotlin/com/example/my_example_test_dart_native/MainActivity.kt b/flutter/realm_flutter/example1/my_example_test_dart_native/android/app/src/main/kotlin/com/example/my_example_test_dart_native/MainActivity.kt new file mode 100644 index 000000000..17163110a --- /dev/null +++ b/flutter/realm_flutter/example1/my_example_test_dart_native/android/app/src/main/kotlin/com/example/my_example_test_dart_native/MainActivity.kt @@ -0,0 +1,6 @@ +package com.example.my_example_test_dart_native + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() { +} diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/android/app/src/main/res/drawable/launch_background.xml b/flutter/realm_flutter/example1/my_example_test_dart_native/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 000000000..304732f88 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example_test_dart_native/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/flutter/realm_flutter/example1/my_example_test_dart_native/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..db77bb4b7 Binary files /dev/null and b/flutter/realm_flutter/example1/my_example_test_dart_native/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/flutter/realm_flutter/example1/my_example_test_dart_native/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..17987b79b Binary files /dev/null and b/flutter/realm_flutter/example1/my_example_test_dart_native/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/flutter/realm_flutter/example1/my_example_test_dart_native/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..09d439148 Binary files /dev/null and b/flutter/realm_flutter/example1/my_example_test_dart_native/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/flutter/realm_flutter/example1/my_example_test_dart_native/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..d5f1c8d34 Binary files /dev/null and b/flutter/realm_flutter/example1/my_example_test_dart_native/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/flutter/realm_flutter/example1/my_example_test_dart_native/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..4d6372eeb Binary files /dev/null and b/flutter/realm_flutter/example1/my_example_test_dart_native/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/android/app/src/main/res/values/styles.xml b/flutter/realm_flutter/example1/my_example_test_dart_native/android/app/src/main/res/values/styles.xml new file mode 100644 index 000000000..1f83a33fd --- /dev/null +++ b/flutter/realm_flutter/example1/my_example_test_dart_native/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/android/app/src/profile/AndroidManifest.xml b/flutter/realm_flutter/example1/my_example_test_dart_native/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 000000000..ffc57aa59 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example_test_dart_native/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/android/build.gradle b/flutter/realm_flutter/example1/my_example_test_dart_native/android/build.gradle new file mode 100644 index 000000000..3100ad2d5 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example_test_dart_native/android/build.gradle @@ -0,0 +1,31 @@ +buildscript { + ext.kotlin_version = '1.3.50' + repositories { + google() + jcenter() + } + + dependencies { + classpath 'com.android.tools.build:gradle:3.5.0' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +allprojects { + repositories { + google() + jcenter() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/android/gradle.properties b/flutter/realm_flutter/example1/my_example_test_dart_native/android/gradle.properties new file mode 100644 index 000000000..94adc3a3f --- /dev/null +++ b/flutter/realm_flutter/example1/my_example_test_dart_native/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx1536M +android.useAndroidX=true +android.enableJetifier=true diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/android/gradle/wrapper/gradle-wrapper.properties b/flutter/realm_flutter/example1/my_example_test_dart_native/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..296b146b7 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example_test_dart_native/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Fri Jun 23 08:50:38 CEST 2017 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/android/settings.gradle b/flutter/realm_flutter/example1/my_example_test_dart_native/android/settings.gradle new file mode 100644 index 000000000..44e62bcf0 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example_test_dart_native/android/settings.gradle @@ -0,0 +1,11 @@ +include ':app' + +def localPropertiesFile = new File(rootProject.projectDir, "local.properties") +def properties = new Properties() + +assert localPropertiesFile.exists() +localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } + +def flutterSdkPath = properties.getProperty("flutter.sdk") +assert flutterSdkPath != null, "flutter.sdk not set in local.properties" +apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/ios/.gitignore b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/.gitignore new file mode 100644 index 000000000..e96ef602b --- /dev/null +++ b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/.gitignore @@ -0,0 +1,32 @@ +*.mode1v3 +*.mode2v3 +*.moved-aside +*.pbxuser +*.perspectivev3 +**/*sync/ +.sconsign.dblite +.tags* +**/.vagrant/ +**/DerivedData/ +Icon? +**/Pods/ +**/.symlinks/ +profile +xcuserdata +**/.generated/ +Flutter/App.framework +Flutter/Flutter.framework +Flutter/Flutter.podspec +Flutter/Generated.xcconfig +Flutter/app.flx +Flutter/app.zip +Flutter/flutter_assets/ +Flutter/flutter_export_environment.sh +ServiceDefinitions.json +Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!default.mode1v3 +!default.mode2v3 +!default.pbxuser +!default.perspectivev3 diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Flutter/AppFrameworkInfo.plist b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 000000000..f2872cf47 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + MinimumOSVersion + 9.0 + + diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Flutter/Debug.xcconfig b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Flutter/Debug.xcconfig new file mode 100644 index 000000000..e8efba114 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Flutter/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "Generated.xcconfig" diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Flutter/Release.xcconfig b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Flutter/Release.xcconfig new file mode 100644 index 000000000..399e9340e --- /dev/null +++ b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Flutter/Release.xcconfig @@ -0,0 +1,2 @@ +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "Generated.xcconfig" diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Podfile b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Podfile new file mode 100644 index 000000000..1e8c3c90a --- /dev/null +++ b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Podfile @@ -0,0 +1,41 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '9.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + end +end diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Podfile.lock b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Podfile.lock new file mode 100644 index 000000000..11ffe03e8 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Podfile.lock @@ -0,0 +1,22 @@ +PODS: + - dart_native (0.0.1): + - Flutter + - Flutter (1.0.0) + +DEPENDENCIES: + - dart_native (from `.symlinks/plugins/dart_native/ios`) + - Flutter (from `Flutter`) + +EXTERNAL SOURCES: + dart_native: + :path: ".symlinks/plugins/dart_native/ios" + Flutter: + :path: Flutter + +SPEC CHECKSUMS: + dart_native: 06e03bcdb1fcf6cbc1046e2ab1642b29e4ae994b + Flutter: 0e3d915762c693b495b44d77113d4970485de6ec + +PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c + +COCOAPODS: 1.8.4 diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner.xcodeproj/project.pbxproj b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 000000000..27b40a51e --- /dev/null +++ b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,566 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 3ECC8F5A6CC4B1326C5D622A /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4BBC11499B54F1E0DB03D3F5 /* Pods_Runner.framework */; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 189292774B2DBB914D2ED04C /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 4BBC11499B54F1E0DB03D3F5 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 65B3C84E4A4D42FDCADBC364 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + 6713CC9741B0384F845FA265 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 3ECC8F5A6CC4B1326C5D622A /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 0C475363F7768C7789BEB449 /* Pods */ = { + isa = PBXGroup; + children = ( + 6713CC9741B0384F845FA265 /* Pods-Runner.debug.xcconfig */, + 189292774B2DBB914D2ED04C /* Pods-Runner.release.xcconfig */, + 65B3C84E4A4D42FDCADBC364 /* Pods-Runner.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; + 2BB4E398A73F630E8C9229A8 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 4BBC11499B54F1E0DB03D3F5 /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + 0C475363F7768C7789BEB449 /* Pods */, + 2BB4E398A73F630E8C9229A8 /* Frameworks */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, + ); + path = Runner; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 497C89FAAD53A16CC84F6B9C /* [CP] Check Pods Manifest.lock */, + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + BA11D62F69EA76F777DC3529 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1020; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 497C89FAAD53A16CC84F6B9C /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-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; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; + BA11D62F69EA76F777DC3529 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", + "${PODS_ROOT}/../Flutter/Flutter.framework", + "${BUILT_PRODUCTS_DIR}/dart_native/dart_native.framework", + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/dart_native.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + 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_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + 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_IMPLICIT_RETAIN_SELF = 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_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "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 = gnu99; + 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 = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.myExampleTestDartNative; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + 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_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + 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_IMPLICIT_RETAIN_SELF = 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_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + 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 = 9.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + 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_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + 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_IMPLICIT_RETAIN_SELF = 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_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "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 = gnu99; + 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 = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.myExampleTestDartNative; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.myExampleTestDartNative; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..1d526a16e --- /dev/null +++ b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 000000000..f9b0d7c5e --- /dev/null +++ b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 000000000..a28140cfd --- /dev/null +++ b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner.xcworkspace/contents.xcworkspacedata b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..21a3cc14c --- /dev/null +++ b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 000000000..f9b0d7c5e --- /dev/null +++ b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/AppDelegate.swift b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/AppDelegate.swift new file mode 100644 index 000000000..70693e4a8 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import UIKit +import Flutter + +@UIApplicationMain +@objc class AppDelegate: FlutterAppDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + GeneratedPluginRegistrant.register(with: self) + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } +} diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000..d36b1fab2 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,122 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "Icon-App-1024x1024@1x.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 000000000..dc9ada472 Binary files /dev/null and b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100644 index 000000000..28c6bf030 Binary files /dev/null and b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 000000000..2ccbfd967 Binary files /dev/null and b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 000000000..f091b6b0b Binary files /dev/null and b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 000000000..4cde12118 Binary files /dev/null and b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 000000000..d0ef06e7e Binary files /dev/null and b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png new file mode 100644 index 000000000..dcdc2306c Binary files /dev/null and b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png new file mode 100644 index 000000000..2ccbfd967 Binary files /dev/null and b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 000000000..c8f9ed8f5 Binary files /dev/null and b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 000000000..a6d6b8609 Binary files /dev/null and b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 000000000..a6d6b8609 Binary files /dev/null and b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 000000000..75b2d164a Binary files /dev/null and b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 000000000..c4df70d39 Binary files /dev/null and b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png new file mode 100644 index 000000000..6a84f41e1 Binary files /dev/null and b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 000000000..d0e1f5853 Binary files /dev/null and b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json new file mode 100644 index 000000000..0bedcf2fd --- /dev/null +++ b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "LaunchImage.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png new file mode 100644 index 000000000..9da19eaca Binary files /dev/null and b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 000000000..9da19eaca Binary files /dev/null and b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 000000000..9da19eaca Binary files /dev/null and b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 000000000..89c2725b7 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -0,0 +1,5 @@ +# Launch Screen Assets + +You can customize the launch screen with your own desired assets by replacing the image files in this directory. + +You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Base.lproj/LaunchScreen.storyboard b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 000000000..f2e259c7c --- /dev/null +++ b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Base.lproj/Main.storyboard b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 000000000..f3c28516f --- /dev/null +++ b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Info.plist b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Info.plist new file mode 100644 index 000000000..5cf0b3357 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Info.plist @@ -0,0 +1,45 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + my_example_test_dart_native + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + + diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Runner-Bridging-Header.h b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 000000000..308a2a560 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example_test_dart_native/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/lib/main.dart b/flutter/realm_flutter/example1/my_example_test_dart_native/lib/main.dart new file mode 100644 index 000000000..11655b668 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example_test_dart_native/lib/main.dart @@ -0,0 +1,117 @@ +import 'package:flutter/material.dart'; + +void main() { + runApp(MyApp()); +} + +class MyApp extends StatelessWidget { + // This widget is the root of your application. + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Flutter Demo', + theme: ThemeData( + // This is the theme of your application. + // + // Try running your application with "flutter run". You'll see the + // application has a blue toolbar. Then, without quitting the app, try + // changing the primarySwatch below to Colors.green and then invoke + // "hot reload" (press "r" in the console where you ran "flutter run", + // or simply save your changes to "hot reload" in a Flutter IDE). + // Notice that the counter didn't reset back to zero; the application + // is not restarted. + primarySwatch: Colors.blue, + // This makes the visual density adapt to the platform that you run + // the app on. For desktop platforms, the controls will be smaller and + // closer together (more dense) than on mobile platforms. + visualDensity: VisualDensity.adaptivePlatformDensity, + ), + home: MyHomePage(title: 'Flutter Demo Home Page'), + ); + } +} + +class MyHomePage extends StatefulWidget { + MyHomePage({Key key, this.title}) : super(key: key); + + // This widget is the home page of your application. It is stateful, meaning + // that it has a State object (defined below) that contains fields that affect + // how it looks. + + // This class is the configuration for the state. It holds the values (in this + // case the title) provided by the parent (in this case the App widget) and + // used by the build method of the State. Fields in a Widget subclass are + // always marked "final". + + final String title; + + @override + _MyHomePageState createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State { + int _counter = 0; + + void _incrementCounter() { + setState(() { + // This call to setState tells the Flutter framework that something has + // changed in this State, which causes it to rerun the build method below + // so that the display can reflect the updated values. If we changed + // _counter without calling setState(), then the build method would not be + // called again, and so nothing would appear to happen. + _counter++; + }); + } + + @override + Widget build(BuildContext context) { + // This method is rerun every time setState is called, for instance as done + // by the _incrementCounter method above. + // + // The Flutter framework has been optimized to make rerunning build methods + // fast, so that you can just rebuild anything that needs updating rather + // than having to individually change instances of widgets. + return Scaffold( + appBar: AppBar( + // Here we take the value from the MyHomePage object that was created by + // the App.build method, and use it to set our appbar title. + title: Text(widget.title), + ), + body: Center( + // Center is a layout widget. It takes a single child and positions it + // in the middle of the parent. + child: Column( + // Column is also a layout widget. It takes a list of children and + // arranges them vertically. By default, it sizes itself to fit its + // children horizontally, and tries to be as tall as its parent. + // + // Invoke "debug painting" (press "p" in the console, choose the + // "Toggle Debug Paint" action from the Flutter Inspector in Android + // Studio, or the "Toggle Debug Paint" command in Visual Studio Code) + // to see the wireframe for each widget. + // + // Column has various properties to control how it sizes itself and + // how it positions its children. Here we use mainAxisAlignment to + // center the children vertically; the main axis here is the vertical + // axis because Columns are vertical (the cross axis would be + // horizontal). + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'You have pushed the button this many times:', + ), + Text( + '$_counter', + style: Theme.of(context).textTheme.headline4, + ), + ], + ), + ), + floatingActionButton: FloatingActionButton( + onPressed: _incrementCounter, + tooltip: 'Increment', + child: Icon(Icons.add), + ), // This trailing comma makes auto-formatting nicer for build methods. + ); + } +} diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/pubspec.lock b/flutter/realm_flutter/example1/my_example_test_dart_native/pubspec.lock new file mode 100644 index 000000000..c5d11e31a --- /dev/null +++ b/flutter/realm_flutter/example1/my_example_test_dart_native/pubspec.lock @@ -0,0 +1,322 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + url: "https://pub.dartlang.org" + source: hosted + version: "7.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + url: "https://pub.dartlang.org" + source: hosted + version: "0.39.17" + args: + dependency: transitive + description: + name: args + url: "https://pub.dartlang.org" + source: hosted + version: "1.6.0" + async: + dependency: transitive + description: + name: async + url: "https://pub.dartlang.org" + source: hosted + version: "2.5.0-nullsafety.1" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0-nullsafety.1" + build: + dependency: transitive + description: + name: build + url: "https://pub.dartlang.org" + source: hosted + version: "1.5.0" + characters: + dependency: transitive + description: + name: characters + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0-nullsafety.3" + charcode: + dependency: transitive + description: + name: charcode + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0-nullsafety.1" + cli_util: + dependency: transitive + description: + name: cli_util + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.0" + clock: + dependency: transitive + description: + name: clock + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0-nullsafety.1" + collection: + dependency: transitive + description: + name: collection + url: "https://pub.dartlang.org" + source: hosted + version: "1.15.0-nullsafety.3" + convert: + dependency: transitive + description: + name: convert + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + crypto: + dependency: transitive + description: + name: crypto + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.5" + csslib: + dependency: transitive + description: + name: csslib + url: "https://pub.dartlang.org" + source: hosted + version: "0.16.2" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" + dart_native: + dependency: "direct main" + description: + name: dart_native + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.4" + dart_native_gen: + dependency: transitive + description: + name: dart_native_gen + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.0" + dart_style: + dependency: transitive + description: + name: dart_style + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.6" + fake_async: + dependency: transitive + description: + name: fake_async + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0-nullsafety.1" + ffi: + dependency: transitive + description: + name: ffi + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.3" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + glob: + dependency: transitive + description: + name: glob + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + html: + dependency: transitive + description: + name: html + url: "https://pub.dartlang.org" + source: hosted + version: "0.14.0+4" + js: + dependency: transitive + description: + name: js + url: "https://pub.dartlang.org" + source: hosted + version: "0.6.2" + logging: + dependency: transitive + description: + name: logging + url: "https://pub.dartlang.org" + source: hosted + version: "0.11.4" + matcher: + dependency: transitive + description: + name: matcher + url: "https://pub.dartlang.org" + source: hosted + version: "0.12.10-nullsafety.1" + meta: + dependency: transitive + description: + name: meta + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.0-nullsafety.3" + node_interop: + dependency: transitive + description: + name: node_interop + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + node_io: + dependency: transitive + description: + name: node_io + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.1" + package_config: + dependency: transitive + description: + name: package_config + url: "https://pub.dartlang.org" + source: hosted + version: "1.9.3" + path: + dependency: transitive + description: + name: path + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.0-nullsafety.1" + pedantic: + dependency: transitive + description: + name: pedantic + url: "https://pub.dartlang.org" + source: hosted + version: "1.9.2" + pub_semver: + dependency: transitive + description: + name: pub_semver + url: "https://pub.dartlang.org" + source: hosted + version: "1.4.4" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_gen: + dependency: transitive + description: + name: source_gen + url: "https://pub.dartlang.org" + source: hosted + version: "0.9.8" + source_span: + dependency: transitive + description: + name: source_span + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.0-nullsafety.2" + stack_trace: + dependency: transitive + description: + name: stack_trace + url: "https://pub.dartlang.org" + source: hosted + version: "1.10.0-nullsafety.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0-nullsafety.1" + string_scanner: + dependency: transitive + description: + name: string_scanner + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0-nullsafety.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0-nullsafety.1" + test_api: + dependency: transitive + description: + name: test_api + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.19-nullsafety.2" + typed_data: + dependency: transitive + description: + name: typed_data + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.0-nullsafety.3" + vector_math: + dependency: transitive + description: + name: vector_math + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0-nullsafety.3" + watcher: + dependency: transitive + description: + name: watcher + url: "https://pub.dartlang.org" + source: hosted + version: "0.9.7+15" + yaml: + dependency: transitive + description: + name: yaml + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.1" +sdks: + dart: ">=2.10.0 <2.11.0" + flutter: ">=1.20.0 <2.0.0" diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/pubspec.yaml b/flutter/realm_flutter/example1/my_example_test_dart_native/pubspec.yaml new file mode 100644 index 000000000..2168bb749 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example_test_dart_native/pubspec.yaml @@ -0,0 +1,78 @@ +name: my_example_test_dart_native +description: A new Flutter project. + +# The following line prevents the package from being accidentally published to +# pub.dev using `pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +# The following defines the version and build number for your application. +# A version number is three numbers separated by dots, like 1.2.43 +# followed by an optional build number separated by a +. +# Both the version and the builder number may be overridden in flutter +# build by specifying --build-name and --build-number, respectively. +# In Android, build-name is used as versionName while build-number used as versionCode. +# Read more about Android versioning at https://developer.android.com/studio/publish/versioning +# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. +# Read more about iOS versioning at +# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html +version: 1.0.0+1 + +environment: + sdk: ">=2.7.0 <3.0.0" + +dependencies: + flutter: + sdk: flutter + + dart_native: any + + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.0 + +dev_dependencies: + flutter_test: + sdk: flutter + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware. + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/custom-fonts/#from-packages diff --git a/flutter/realm_flutter/example1/my_example_test_dart_native/test/widget_test.dart b/flutter/realm_flutter/example1/my_example_test_dart_native/test/widget_test.dart new file mode 100644 index 000000000..24178a060 --- /dev/null +++ b/flutter/realm_flutter/example1/my_example_test_dart_native/test/widget_test.dart @@ -0,0 +1,30 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility that Flutter provides. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:my_example_test_dart_native/main.dart'; + +void main() { + testWidgets('Counter increments smoke test', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(MyApp()); + + // Verify that our counter starts at 0. + expect(find.text('0'), findsOneWidget); + expect(find.text('1'), findsNothing); + + // Tap the '+' icon and trigger a frame. + await tester.tap(find.byIcon(Icons.add)); + await tester.pump(); + + // Verify that our counter has incremented. + expect(find.text('0'), findsNothing); + expect(find.text('1'), findsOneWidget); + }); +} diff --git a/flutter/realm_flutter/ios/.gitignore b/flutter/realm_flutter/ios/.gitignore index aa479fd3c..0d0e0340a 100644 --- a/flutter/realm_flutter/ios/.gitignore +++ b/flutter/realm_flutter/ios/.gitignore @@ -34,4 +34,6 @@ Icon? .tags* /Flutter/Generated.xcconfig -/Flutter/flutter_export_environment.sh \ No newline at end of file +/Flutter/flutter_export_environment.sh +realm-dart-src/* +Frameworks/* \ No newline at end of file diff --git a/flutter/realm_flutter/ios/Classes/dart_api_dl.cpp b/flutter/realm_flutter/ios/Classes/dart_api_dl.cpp new file mode 100644 index 000000000..70c595899 --- /dev/null +++ b/flutter/realm_flutter/ios/Classes/dart_api_dl.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file + * for details. All rights reserved. Use of this source code is governed by a + * BSD-style license that can be found in the LICENSE file. + */ + +#include "include/dart_api_dl.h" +#include "include/dart_version.h" +#include "include/internal/dart_api_dl_impl.h" + +#include + +void init_realm(); + +#define DART_API_DL_DEFINITIONS(name, R, A) name##_Type name##_DL = NULL; + +DART_API_ALL_DL_SYMBOLS(DART_API_DL_DEFINITIONS) + +#undef DART_API_DL_DEFINITIONS + +typedef void (*DartApiEntry_function)(); + +DartApiEntry_function FindFunctionPointer(const DartApiEntry* entries, + const char* name) { + while (entries->name != NULL) { + if (strcmp(entries->name, name) == 0) return entries->function; + entries++; + } + return NULL; +} + +intptr_t Dart_InitializeApiDL(void* data) { + DartApi* dart_api_data = (DartApi*)data; + + if (dart_api_data->major != DART_API_DL_MAJOR_VERSION) { + // If the DartVM we're running on does not have the same version as this + // file was compiled against, refuse to initialize. The symbols are not + // compatible. + return -1; + } + // Minor versions are allowed to be different. + // If the DartVM has a higher minor version, it will provide more symbols + // than we initialize here. + // If the DartVM has a lower minor version, it will not provide all symbols. + // In that case, we leave the missing symbols un-initialized. Those symbols + // should not be used by the Dart and native code. The client is responsible + // for checking the minor version number himself based on which symbols it + // is using. + // (If we would error out on this case, recompiling native code against a + // newer SDK would break all uses on older SDKs, which is too strict.) + + const DartApiEntry* dart_api_function_pointers = dart_api_data->functions; + +#define DART_API_DL_INIT(name, R, A) \ + name##_DL = \ + (name##_Type)(FindFunctionPointer(dart_api_function_pointers, #name)); + DART_API_ALL_DL_SYMBOLS(DART_API_DL_INIT) +#undef DART_API_DL_INIT + + + + init_realm(); + + return 0; +} diff --git a/flutter/realm_flutter/ios/Classes/include/dart_api.h b/flutter/realm_flutter/ios/Classes/include/dart_api.h new file mode 100644 index 000000000..821ae121a --- /dev/null +++ b/flutter/realm_flutter/ios/Classes/include/dart_api.h @@ -0,0 +1,3886 @@ +/* + * Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file + * for details. All rights reserved. Use of this source code is governed by a + * BSD-style license that can be found in the LICENSE file. + */ + +#ifndef RUNTIME_INCLUDE_DART_API_H_ +#define RUNTIME_INCLUDE_DART_API_H_ + +/** \mainpage Dart Embedding API Reference + * + * This reference describes the Dart Embedding API, which is used to embed the + * Dart Virtual Machine within C/C++ applications. + * + * This reference is generated from the header include/dart_api.h. + */ + +/* __STDC_FORMAT_MACROS has to be defined before including to + * enable platform independent printf format specifiers. */ +#ifndef __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS +#endif + +#include +#include +#include + +#ifdef __cplusplus +#define DART_EXTERN_C extern "C" +#else +#define DART_EXTERN_C +#endif + +#if defined(__CYGWIN__) +#error Tool chain and platform not supported. +#elif defined(_WIN32) +#if defined(DART_SHARED_LIB) +#define DART_EXPORT DART_EXTERN_C __declspec(dllexport) +#else +#define DART_EXPORT DART_EXTERN_C +#endif +#else +#if __GNUC__ >= 4 +#if defined(DART_SHARED_LIB) +#define DART_EXPORT \ + DART_EXTERN_C __attribute__((visibility("default"))) __attribute((used)) +#else +#define DART_EXPORT DART_EXTERN_C +#endif +#else +#error Tool chain not supported. +#endif +#endif + +#if __GNUC__ +#define DART_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) +#elif _MSC_VER +#define DART_WARN_UNUSED_RESULT _Check_return_ +#else +#define DART_WARN_UNUSED_RESULT +#endif + +/* + * ======= + * Handles + * ======= + */ + +/** + * An isolate is the unit of concurrency in Dart. Each isolate has + * its own memory and thread of control. No state is shared between + * isolates. Instead, isolates communicate by message passing. + * + * Each thread keeps track of its current isolate, which is the + * isolate which is ready to execute on the current thread. The + * current isolate may be NULL, in which case no isolate is ready to + * execute. Most of the Dart apis require there to be a current + * isolate in order to function without error. The current isolate is + * set by any call to Dart_CreateIsolateGroup or Dart_EnterIsolate. + */ +typedef struct _Dart_Isolate* Dart_Isolate; +typedef struct _Dart_IsolateGroup* Dart_IsolateGroup; + +/** + * An object reference managed by the Dart VM garbage collector. + * + * Because the garbage collector may move objects, it is unsafe to + * refer to objects directly. Instead, we refer to objects through + * handles, which are known to the garbage collector and updated + * automatically when the object is moved. Handles should be passed + * by value (except in cases like out-parameters) and should never be + * allocated on the heap. + * + * Most functions in the Dart Embedding API return a handle. When a + * function completes normally, this will be a valid handle to an + * object in the Dart VM heap. This handle may represent the result of + * the operation or it may be a special valid handle used merely to + * indicate successful completion. Note that a valid handle may in + * some cases refer to the null object. + * + * --- Error handles --- + * + * When a function encounters a problem that prevents it from + * completing normally, it returns an error handle (See Dart_IsError). + * An error handle has an associated error message that gives more + * details about the problem (See Dart_GetError). + * + * There are four kinds of error handles that can be produced, + * depending on what goes wrong: + * + * - Api error handles are produced when an api function is misused. + * This happens when a Dart embedding api function is called with + * invalid arguments or in an invalid context. + * + * - Unhandled exception error handles are produced when, during the + * execution of Dart code, an exception is thrown but not caught. + * Prototypically this would occur during a call to Dart_Invoke, but + * it can occur in any function which triggers the execution of Dart + * code (for example, Dart_ToString). + * + * An unhandled exception error provides access to an exception and + * stacktrace via the functions Dart_ErrorGetException and + * Dart_ErrorGetStackTrace. + * + * - Compilation error handles are produced when, during the execution + * of Dart code, a compile-time error occurs. As above, this can + * occur in any function which triggers the execution of Dart code. + * + * - Fatal error handles are produced when the system wants to shut + * down the current isolate. + * + * --- Propagating errors --- + * + * When an error handle is returned from the top level invocation of + * Dart code in a program, the embedder must handle the error as they + * see fit. Often, the embedder will print the error message produced + * by Dart_Error and exit the program. + * + * When an error is returned while in the body of a native function, + * it can be propagated up the call stack by calling + * Dart_PropagateError, Dart_SetReturnValue, or Dart_ThrowException. + * Errors should be propagated unless there is a specific reason not + * to. If an error is not propagated then it is ignored. For + * example, if an unhandled exception error is ignored, that + * effectively "catches" the unhandled exception. Fatal errors must + * always be propagated. + * + * When an error is propagated, any current scopes created by + * Dart_EnterScope will be exited. + * + * Using Dart_SetReturnValue to propagate an exception is somewhat + * more convenient than using Dart_PropagateError, and should be + * preferred for reasons discussed below. + * + * Dart_PropagateError and Dart_ThrowException do not return. Instead + * they transfer control non-locally using a setjmp-like mechanism. + * This can be inconvenient if you have resources that you need to + * clean up before propagating the error. + * + * When relying on Dart_PropagateError, we often return error handles + * rather than propagating them from helper functions. Consider the + * following contrived example: + * + * 1 Dart_Handle isLongStringHelper(Dart_Handle arg) { + * 2 intptr_t* length = 0; + * 3 result = Dart_StringLength(arg, &length); + * 4 if (Dart_IsError(result)) { + * 5 return result; + * 6 } + * 7 return Dart_NewBoolean(length > 100); + * 8 } + * 9 + * 10 void NativeFunction_isLongString(Dart_NativeArguments args) { + * 11 Dart_EnterScope(); + * 12 AllocateMyResource(); + * 13 Dart_Handle arg = Dart_GetNativeArgument(args, 0); + * 14 Dart_Handle result = isLongStringHelper(arg); + * 15 if (Dart_IsError(result)) { + * 16 FreeMyResource(); + * 17 Dart_PropagateError(result); + * 18 abort(); // will not reach here + * 19 } + * 20 Dart_SetReturnValue(result); + * 21 FreeMyResource(); + * 22 Dart_ExitScope(); + * 23 } + * + * In this example, we have a native function which calls a helper + * function to do its work. On line 5, the helper function could call + * Dart_PropagateError, but that would not give the native function a + * chance to call FreeMyResource(), causing a leak. Instead, the + * helper function returns the error handle to the caller, giving the + * caller a chance to clean up before propagating the error handle. + * + * When an error is propagated by calling Dart_SetReturnValue, the + * native function will be allowed to complete normally and then the + * exception will be propagated only once the native call + * returns. This can be convenient, as it allows the C code to clean + * up normally. + * + * The example can be written more simply using Dart_SetReturnValue to + * propagate the error. + * + * 1 Dart_Handle isLongStringHelper(Dart_Handle arg) { + * 2 intptr_t* length = 0; + * 3 result = Dart_StringLength(arg, &length); + * 4 if (Dart_IsError(result)) { + * 5 return result + * 6 } + * 7 return Dart_NewBoolean(length > 100); + * 8 } + * 9 + * 10 void NativeFunction_isLongString(Dart_NativeArguments args) { + * 11 Dart_EnterScope(); + * 12 AllocateMyResource(); + * 13 Dart_Handle arg = Dart_GetNativeArgument(args, 0); + * 14 Dart_SetReturnValue(isLongStringHelper(arg)); + * 15 FreeMyResource(); + * 16 Dart_ExitScope(); + * 17 } + * + * In this example, the call to Dart_SetReturnValue on line 14 will + * either return the normal return value or the error (potentially + * generated on line 3). The call to FreeMyResource on line 15 will + * execute in either case. + * + * --- Local and persistent handles --- + * + * Local handles are allocated within the current scope (see + * Dart_EnterScope) and go away when the current scope exits. Unless + * otherwise indicated, callers should assume that all functions in + * the Dart embedding api return local handles. + * + * Persistent handles are allocated within the current isolate. They + * can be used to store objects across scopes. Persistent handles have + * the lifetime of the current isolate unless they are explicitly + * deallocated (see Dart_DeletePersistentHandle). + * The type Dart_Handle represents a handle (both local and persistent). + * The type Dart_PersistentHandle is a Dart_Handle and it is used to + * document that a persistent handle is expected as a parameter to a call + * or the return value from a call is a persistent handle. + * + * FinalizableHandles are persistent handles which are auto deleted when + * the object is garbage collected. It is never safe to use these handles + * unless you know the object is still reachable. + * + * WeakPersistentHandles are persistent handles which are auto deleted + * when the object is garbage collected. + */ +typedef struct _Dart_Handle* Dart_Handle; +typedef Dart_Handle Dart_PersistentHandle; +typedef struct _Dart_WeakPersistentHandle* Dart_WeakPersistentHandle; +typedef struct _Dart_FinalizableHandle* Dart_FinalizableHandle; +// These structs are versioned by DART_API_DL_MAJOR_VERSION, bump the +// version when changing this struct. + +typedef void (*Dart_WeakPersistentHandleFinalizer)( + void* isolate_callback_data, + Dart_WeakPersistentHandle handle, + void* peer); +typedef void (*Dart_HandleFinalizer)(void* isolate_callback_data, void* peer); + +/** + * Is this an error handle? + * + * Requires there to be a current isolate. + */ +DART_EXPORT bool Dart_IsError(Dart_Handle handle); + +/** + * Is this an api error handle? + * + * Api error handles are produced when an api function is misused. + * This happens when a Dart embedding api function is called with + * invalid arguments or in an invalid context. + * + * Requires there to be a current isolate. + */ +DART_EXPORT bool Dart_IsApiError(Dart_Handle handle); + +/** + * Is this an unhandled exception error handle? + * + * Unhandled exception error handles are produced when, during the + * execution of Dart code, an exception is thrown but not caught. + * This can occur in any function which triggers the execution of Dart + * code. + * + * See Dart_ErrorGetException and Dart_ErrorGetStackTrace. + * + * Requires there to be a current isolate. + */ +DART_EXPORT bool Dart_IsUnhandledExceptionError(Dart_Handle handle); + +/** + * Is this a compilation error handle? + * + * Compilation error handles are produced when, during the execution + * of Dart code, a compile-time error occurs. This can occur in any + * function which triggers the execution of Dart code. + * + * Requires there to be a current isolate. + */ +DART_EXPORT bool Dart_IsCompilationError(Dart_Handle handle); + +/** + * Is this a fatal error handle? + * + * Fatal error handles are produced when the system wants to shut down + * the current isolate. + * + * Requires there to be a current isolate. + */ +DART_EXPORT bool Dart_IsFatalError(Dart_Handle handle); + +/** + * Gets the error message from an error handle. + * + * Requires there to be a current isolate. + * + * \return A C string containing an error message if the handle is + * error. An empty C string ("") if the handle is valid. This C + * String is scope allocated and is only valid until the next call + * to Dart_ExitScope. +*/ +DART_EXPORT const char* Dart_GetError(Dart_Handle handle); + +/** + * Is this an error handle for an unhandled exception? + */ +DART_EXPORT bool Dart_ErrorHasException(Dart_Handle handle); + +/** + * Gets the exception Object from an unhandled exception error handle. + */ +DART_EXPORT Dart_Handle Dart_ErrorGetException(Dart_Handle handle); + +/** + * Gets the stack trace Object from an unhandled exception error handle. + */ +DART_EXPORT Dart_Handle Dart_ErrorGetStackTrace(Dart_Handle handle); + +/** + * Produces an api error handle with the provided error message. + * + * Requires there to be a current isolate. + * + * \param error the error message. + */ +DART_EXPORT Dart_Handle Dart_NewApiError(const char* error); +DART_EXPORT Dart_Handle Dart_NewCompilationError(const char* error); + +/** + * Produces a new unhandled exception error handle. + * + * Requires there to be a current isolate. + * + * \param exception An instance of a Dart object to be thrown or + * an ApiError or CompilationError handle. + * When an ApiError or CompilationError handle is passed in + * a string object of the error message is created and it becomes + * the Dart object to be thrown. + */ +DART_EXPORT Dart_Handle Dart_NewUnhandledExceptionError(Dart_Handle exception); + +/** + * Propagates an error. + * + * If the provided handle is an unhandled exception error, this + * function will cause the unhandled exception to be rethrown. This + * will proceed in the standard way, walking up Dart frames until an + * appropriate 'catch' block is found, executing 'finally' blocks, + * etc. + * + * If the error is not an unhandled exception error, we will unwind + * the stack to the next C frame. Intervening Dart frames will be + * discarded; specifically, 'finally' blocks will not execute. This + * is the standard way that compilation errors (and the like) are + * handled by the Dart runtime. + * + * In either case, when an error is propagated any current scopes + * created by Dart_EnterScope will be exited. + * + * See the additional discussion under "Propagating Errors" at the + * beginning of this file. + * + * \param An error handle (See Dart_IsError) + * + * \return On success, this function does not return. On failure, the + * process is terminated. + */ +DART_EXPORT void Dart_PropagateError(Dart_Handle handle); + +/** + * Converts an object to a string. + * + * May generate an unhandled exception error. + * + * \return The converted string if no error occurs during + * the conversion. If an error does occur, an error handle is + * returned. + */ +DART_EXPORT Dart_Handle Dart_ToString(Dart_Handle object); + +/** + * Checks to see if two handles refer to identically equal objects. + * + * If both handles refer to instances, this is equivalent to using the top-level + * function identical() from dart:core. Otherwise, returns whether the two + * argument handles refer to the same object. + * + * \param obj1 An object to be compared. + * \param obj2 An object to be compared. + * + * \return True if the objects are identically equal. False otherwise. + */ +DART_EXPORT bool Dart_IdentityEquals(Dart_Handle obj1, Dart_Handle obj2); + +/** + * Allocates a handle in the current scope from a persistent handle. + */ +DART_EXPORT Dart_Handle Dart_HandleFromPersistent(Dart_PersistentHandle object); + +/** + * Allocates a handle in the current scope from a weak persistent handle. + */ +DART_EXPORT Dart_Handle +Dart_HandleFromWeakPersistent(Dart_WeakPersistentHandle object); + +/** + * Allocates a persistent handle for an object. + * + * This handle has the lifetime of the current isolate unless it is + * explicitly deallocated by calling Dart_DeletePersistentHandle. + * + * Requires there to be a current isolate. + */ +DART_EXPORT Dart_PersistentHandle Dart_NewPersistentHandle(Dart_Handle object); + +/** + * Assign value of local handle to a persistent handle. + * + * Requires there to be a current isolate. + * + * \param obj1 A persistent handle whose value needs to be set. + * \param obj2 An object whose value needs to be set to the persistent handle. + * + * \return Success if the persistent handle was set + * Otherwise, returns an error. + */ +DART_EXPORT void Dart_SetPersistentHandle(Dart_PersistentHandle obj1, + Dart_Handle obj2); + +/** + * Deallocates a persistent handle. + * + * Requires there to be a current isolate group. + */ +DART_EXPORT void Dart_DeletePersistentHandle(Dart_PersistentHandle object); + +/** + * Allocates a weak persistent handle for an object. + * + * This handle has the lifetime of the current isolate unless the object + * pointed to by the handle is garbage collected, in this case the VM + * automatically deletes the handle after invoking the callback associated + * with the handle. The handle can also be explicitly deallocated by + * calling Dart_DeleteWeakPersistentHandle. + * + * If the object becomes unreachable the callback is invoked with the weak + * persistent handle and the peer as arguments. The callback can be executed on + * any thread, will have an isolate group, but will not have a current isolate. + * The callback can only call Dart_DeletePersistentHandle or + * Dart_DeleteWeakPersistentHandle. The callback must not call + * Dart_DeleteWeakPersistentHandle for the handle being finalized, as it is + * automatically deleted by the VM after the callback returns. This gives the + * embedder the ability to cleanup data associated with the object and clear + * out any cached references to the handle. All references to this handle after + * the callback will be invalid. It is illegal to call into the VM with any + * other Dart_* functions from the callback. If the handle is deleted before + * the object becomes unreachable, the callback is never invoked. + * + * Requires there to be a current isolate. + * + * \param object An object. + * \param peer A pointer to a native object or NULL. This value is + * provided to callback when it is invoked. + * \param external_allocation_size The number of externally allocated + * bytes for peer. Used to inform the garbage collector. + * \param callback A function pointer that will be invoked sometime + * after the object is garbage collected, unless the handle has been deleted. + * A valid callback needs to be specified it cannot be NULL. + * + * \return The weak persistent handle or NULL. NULL is returned in case of bad + * parameters. + */ +DART_EXPORT Dart_WeakPersistentHandle +Dart_NewWeakPersistentHandle(Dart_Handle object, + void* peer, + intptr_t external_allocation_size, + Dart_WeakPersistentHandleFinalizer callback); + +/** + * Deletes the given weak persistent [object] handle. + * + * Requires there to be a current isolate group. + */ +DART_EXPORT void Dart_DeleteWeakPersistentHandle( + Dart_WeakPersistentHandle object); + +/** + * Updates the external memory size for the given weak persistent handle. + * + * May trigger garbage collection. + */ +DART_EXPORT void Dart_UpdateExternalSize(Dart_WeakPersistentHandle object, + intptr_t external_allocation_size); + +/** + * Allocates a finalizable handle for an object. + * + * This handle has the lifetime of the current isolate group unless the object + * pointed to by the handle is garbage collected, in this case the VM + * automatically deletes the handle after invoking the callback associated + * with the handle. The handle can also be explicitly deallocated by + * calling Dart_DeleteFinalizableHandle. + * + * If the object becomes unreachable the callback is invoked with the + * the peer as argument. The callback can be executed on any thread, will have + * an isolate group, but will not have a current isolate. The callback can only + * call Dart_DeletePersistentHandle or Dart_DeleteWeakPersistentHandle. + * This gives the embedder the ability to cleanup data associated with the + * object and clear out any cached references to the handle. All references to + * this handle after the callback will be invalid. It is illegal to call into + * the VM with any other Dart_* functions from the callback. If the handle is + * deleted before the object becomes unreachable, the callback is never + * invoked. + * + * Requires there to be a current isolate. + * + * \param object An object. + * \param peer A pointer to a native object or NULL. This value is + * provided to callback when it is invoked. + * \param external_allocation_size The number of externally allocated + * bytes for peer. Used to inform the garbage collector. + * \param callback A function pointer that will be invoked sometime + * after the object is garbage collected, unless the handle has been deleted. + * A valid callback needs to be specified it cannot be NULL. + * + * \return The finalizable handle or NULL. NULL is returned in case of bad + * parameters. + */ +DART_EXPORT Dart_FinalizableHandle +Dart_NewFinalizableHandle(Dart_Handle object, + void* peer, + intptr_t external_allocation_size, + Dart_HandleFinalizer callback); + +/** + * Deletes the given finalizable [object] handle. + * + * The caller has to provide the actual Dart object the handle was created from + * to prove the object (and therefore the finalizable handle) is still alive. + * + * Requires there to be a current isolate. + */ +DART_EXPORT void Dart_DeleteFinalizableHandle(Dart_FinalizableHandle object, + Dart_Handle strong_ref_to_object); + +/** + * Updates the external memory size for the given finalizable handle. + * + * The caller has to provide the actual Dart object the handle was created from + * to prove the object (and therefore the finalizable handle) is still alive. + * + * May trigger garbage collection. + */ +DART_EXPORT void Dart_UpdateFinalizableExternalSize( + Dart_FinalizableHandle object, + Dart_Handle strong_ref_to_object, + intptr_t external_allocation_size); + +/* + * ========================== + * Initialization and Globals + * ========================== + */ + +/** + * Gets the version string for the Dart VM. + * + * The version of the Dart VM can be accessed without initializing the VM. + * + * \return The version string for the embedded Dart VM. + */ +DART_EXPORT const char* Dart_VersionString(); + +typedef struct { + const char* library_uri; + const char* class_name; + const char* function_name; +} Dart_QualifiedFunctionName; + +/** + * Isolate specific flags are set when creating a new isolate using the + * Dart_IsolateFlags structure. + * + * Current version of flags is encoded in a 32-bit integer with 16 bits used + * for each part. + */ + +#define DART_FLAGS_CURRENT_VERSION (0x0000000c) + +typedef struct { + int32_t version; + bool enable_asserts; + bool use_field_guards; + bool use_osr; + bool obfuscate; + Dart_QualifiedFunctionName* entry_points; + bool load_vmservice_library; + bool copy_parent_code; + bool null_safety; + bool is_system_isolate; +} Dart_IsolateFlags; + +/** + * Initialize Dart_IsolateFlags with correct version and default values. + */ +DART_EXPORT void Dart_IsolateFlagsInitialize(Dart_IsolateFlags* flags); + +/** + * An isolate creation and initialization callback function. + * + * This callback, provided by the embedder, is called when the VM + * needs to create an isolate. The callback should create an isolate + * by calling Dart_CreateIsolateGroup and load any scripts required for + * execution. + * + * This callback may be called on a different thread than the one + * running the parent isolate. + * + * When the function returns NULL, it is the responsibility of this + * function to ensure that Dart_ShutdownIsolate has been called if + * required (for example, if the isolate was created successfully by + * Dart_CreateIsolateGroup() but the root library fails to load + * successfully, then the function should call Dart_ShutdownIsolate + * before returning). + * + * When the function returns NULL, the function should set *error to + * a malloc-allocated buffer containing a useful error message. The + * caller of this function (the VM) will make sure that the buffer is + * freed. + * + * \param script_uri The uri of the main source file or snapshot to load. + * Either the URI of the parent isolate set in Dart_CreateIsolateGroup for + * Isolate.spawn, or the argument to Isolate.spawnUri canonicalized by the + * library tag handler of the parent isolate. + * The callback is responsible for loading the program by a call to + * Dart_LoadScriptFromKernel. + * \param main The name of the main entry point this isolate will + * eventually run. This is provided for advisory purposes only to + * improve debugging messages. The main function is not invoked by + * this function. + * \param package_root Ignored. + * \param package_config Uri of the package configuration file (either in format + * of .packages or .dart_tool/package_config.json) for this isolate + * to resolve package imports against. If this parameter is not passed the + * package resolution of the parent isolate should be used. + * \param flags Default flags for this isolate being spawned. Either inherited + * from the spawning isolate or passed as parameters when spawning the + * isolate from Dart code. + * \param isolate_data The isolate data which was passed to the + * parent isolate when it was created by calling Dart_CreateIsolateGroup(). + * \param error A structure into which the embedder can place a + * C string containing an error message in the case of failures. + * + * \return The embedder returns NULL if the creation and + * initialization was not successful and the isolate if successful. + */ +typedef Dart_Isolate (*Dart_IsolateGroupCreateCallback)( + const char* script_uri, + const char* main, + const char* package_root, + const char* package_config, + Dart_IsolateFlags* flags, + void* isolate_data, + char** error); + +/** + * An isolate initialization callback function. + * + * This callback, provided by the embedder, is called when the VM has created an + * isolate within an existing isolate group (i.e. from the same source as an + * existing isolate). + * + * The callback should setup native resolvers and might want to set a custom + * message handler via [Dart_SetMessageNotifyCallback] and mark the isolate as + * runnable. + * + * This callback may be called on a different thread than the one + * running the parent isolate. + * + * When the function returns `false`, it is the responsibility of this + * function to ensure that `Dart_ShutdownIsolate` has been called. + * + * When the function returns `false`, the function should set *error to + * a malloc-allocated buffer containing a useful error message. The + * caller of this function (the VM) will make sure that the buffer is + * freed. + * + * \param child_isolate_data The callback data to associate with the new + * child isolate. + * \param error A structure into which the embedder can place a + * C string containing an error message in the case the initialization fails. + * + * \return The embedder returns true if the initialization was successful and + * false otherwise (in which case the VM will terminate the isolate). + */ +typedef bool (*Dart_InitializeIsolateCallback)(void** child_isolate_data, + char** error); + +/** + * An isolate unhandled exception callback function. + * + * This callback has been DEPRECATED. + */ +typedef void (*Dart_IsolateUnhandledExceptionCallback)(Dart_Handle error); + +/** + * An isolate shutdown callback function. + * + * This callback, provided by the embedder, is called before the vm + * shuts down an isolate. The isolate being shutdown will be the current + * isolate. It is safe to run Dart code. + * + * This function should be used to dispose of native resources that + * are allocated to an isolate in order to avoid leaks. + * + * \param isolate_group_data The same callback data which was passed to the + * isolate group when it was created. + * \param isolate_data The same callback data which was passed to the isolate + * when it was created. + */ +typedef void (*Dart_IsolateShutdownCallback)(void* isolate_group_data, + void* isolate_data); + +/** + * An isolate cleanup callback function. + * + * This callback, provided by the embedder, is called after the vm + * shuts down an isolate. There will be no current isolate and it is *not* + * safe to run Dart code. + * + * This function should be used to dispose of native resources that + * are allocated to an isolate in order to avoid leaks. + * + * \param isolate_group_data The same callback data which was passed to the + * isolate group when it was created. + * \param isolate_data The same callback data which was passed to the isolate + * when it was created. + */ +typedef void (*Dart_IsolateCleanupCallback)(void* isolate_group_data, + void* isolate_data); + +/** + * An isolate group cleanup callback function. + * + * This callback, provided by the embedder, is called after the vm + * shuts down an isolate group. + * + * This function should be used to dispose of native resources that + * are allocated to an isolate in order to avoid leaks. + * + * \param isolate_group_data The same callback data which was passed to the + * isolate group when it was created. + * + */ +typedef void (*Dart_IsolateGroupCleanupCallback)(void* isolate_group_data); + +/** + * A thread death callback function. + * This callback, provided by the embedder, is called before a thread in the + * vm thread pool exits. + * This function could be used to dispose of native resources that + * are associated and attached to the thread, in order to avoid leaks. + */ +typedef void (*Dart_ThreadExitCallback)(); + +/** + * Callbacks provided by the embedder for file operations. If the + * embedder does not allow file operations these callbacks can be + * NULL. + * + * Dart_FileOpenCallback - opens a file for reading or writing. + * \param name The name of the file to open. + * \param write A boolean variable which indicates if the file is to + * opened for writing. If there is an existing file it needs to truncated. + * + * Dart_FileReadCallback - Read contents of file. + * \param data Buffer allocated in the callback into which the contents + * of the file are read into. It is the responsibility of the caller to + * free this buffer. + * \param file_length A variable into which the length of the file is returned. + * In the case of an error this value would be -1. + * \param stream Handle to the opened file. + * + * Dart_FileWriteCallback - Write data into file. + * \param data Buffer which needs to be written into the file. + * \param length Length of the buffer. + * \param stream Handle to the opened file. + * + * Dart_FileCloseCallback - Closes the opened file. + * \param stream Handle to the opened file. + * + */ +typedef void* (*Dart_FileOpenCallback)(const char* name, bool write); + +typedef void (*Dart_FileReadCallback)(uint8_t** data, + intptr_t* file_length, + void* stream); + +typedef void (*Dart_FileWriteCallback)(const void* data, + intptr_t length, + void* stream); + +typedef void (*Dart_FileCloseCallback)(void* stream); + +typedef bool (*Dart_EntropySource)(uint8_t* buffer, intptr_t length); + +/** + * Callback provided by the embedder that is used by the vmservice isolate + * to request the asset archive. The asset archive must be an uncompressed tar + * archive that is stored in a Uint8List. + * + * If the embedder has no vmservice isolate assets, the callback can be NULL. + * + * \return The embedder must return a handle to a Uint8List containing an + * uncompressed tar archive or null. + */ +typedef Dart_Handle (*Dart_GetVMServiceAssetsArchive)(); + +/** + * The current version of the Dart_InitializeFlags. Should be incremented every + * time Dart_InitializeFlags changes in a binary incompatible way. + */ +#define DART_INITIALIZE_PARAMS_CURRENT_VERSION (0x00000004) + +/** Forward declaration */ +struct Dart_CodeObserver; + +/** + * Callback provided by the embedder that is used by the VM to notify on code + * object creation, *before* it is invoked the first time. + * This is useful for embedders wanting to e.g. keep track of PCs beyond + * the lifetime of the garbage collected code objects. + * Note that an address range may be used by more than one code object over the + * lifecycle of a process. Clients of this function should record timestamps for + * these compilation events and when collecting PCs to disambiguate reused + * address ranges. + */ +typedef void (*Dart_OnNewCodeCallback)(struct Dart_CodeObserver* observer, + const char* name, + uintptr_t base, + uintptr_t size); + +typedef struct Dart_CodeObserver { + void* data; + + Dart_OnNewCodeCallback on_new_code; +} Dart_CodeObserver; + +/** + * Describes how to initialize the VM. Used with Dart_Initialize. + * + * \param version Identifies the version of the struct used by the client. + * should be initialized to DART_INITIALIZE_PARAMS_CURRENT_VERSION. + * \param vm_isolate_snapshot A buffer containing a snapshot of the VM isolate + * or NULL if no snapshot is provided. If provided, the buffer must remain + * valid until Dart_Cleanup returns. + * \param instructions_snapshot A buffer containing a snapshot of precompiled + * instructions, or NULL if no snapshot is provided. If provided, the buffer + * must remain valid until Dart_Cleanup returns. + * \param initialize_isolate A function to be called during isolate + * initialization inside an existing isolate group. + * See Dart_InitializeIsolateCallback. + * \param create_group A function to be called during isolate group creation. + * See Dart_IsolateGroupCreateCallback. + * \param shutdown A function to be called right before an isolate is shutdown. + * See Dart_IsolateShutdownCallback. + * \param cleanup A function to be called after an isolate was shutdown. + * See Dart_IsolateCleanupCallback. + * \param cleanup_group A function to be called after an isolate group is shutdown. + * See Dart_IsolateGroupCleanupCallback. + * \param get_service_assets A function to be called by the service isolate when + * it requires the vmservice assets archive. + * See Dart_GetVMServiceAssetsArchive. + * \param code_observer An external code observer callback function. + * The observer can be invoked as early as during the Dart_Initialize() call. + */ +typedef struct { + int32_t version; + const uint8_t* vm_snapshot_data; + const uint8_t* vm_snapshot_instructions; + Dart_IsolateGroupCreateCallback create_group; + Dart_InitializeIsolateCallback initialize_isolate; + Dart_IsolateShutdownCallback shutdown_isolate; + Dart_IsolateCleanupCallback cleanup_isolate; + Dart_IsolateGroupCleanupCallback cleanup_group; + Dart_ThreadExitCallback thread_exit; + Dart_FileOpenCallback file_open; + Dart_FileReadCallback file_read; + Dart_FileWriteCallback file_write; + Dart_FileCloseCallback file_close; + Dart_EntropySource entropy_source; + Dart_GetVMServiceAssetsArchive get_service_assets; + bool start_kernel_isolate; + Dart_CodeObserver* code_observer; +} Dart_InitializeParams; + +/** + * Initializes the VM. + * + * \param flags A struct containing initialization information. The version + * field of the struct must be DART_INITIALIZE_PARAMS_CURRENT_VERSION. + * + * \return NULL if initialization is successful. Returns an error message + * otherwise. The caller is responsible for freeing the error message. + */ +DART_EXPORT DART_WARN_UNUSED_RESULT char* Dart_Initialize( + Dart_InitializeParams* params); + +/** + * Cleanup state in the VM before process termination. + * + * \return NULL if cleanup is successful. Returns an error message otherwise. + * The caller is responsible for freeing the error message. + * + * NOTE: This function must not be called on a thread that was created by the VM + * itself. + */ +DART_EXPORT DART_WARN_UNUSED_RESULT char* Dart_Cleanup(); + +/** + * Sets command line flags. Should be called before Dart_Initialize. + * + * \param argc The length of the arguments array. + * \param argv An array of arguments. + * + * \return NULL if successful. Returns an error message otherwise. + * The caller is responsible for freeing the error message. + * + * NOTE: This call does not store references to the passed in c-strings. + */ +DART_EXPORT DART_WARN_UNUSED_RESULT char* Dart_SetVMFlags(int argc, + const char** argv); + +/** + * Returns true if the named VM flag is of boolean type, specified, and set to + * true. + * + * \param flag_name The name of the flag without leading punctuation + * (example: "enable_asserts"). + */ +DART_EXPORT bool Dart_IsVMFlagSet(const char* flag_name); + +/* + * ======== + * Isolates + * ======== + */ + +/** + * Creates a new isolate. The new isolate becomes the current isolate. + * + * A snapshot can be used to restore the VM quickly to a saved state + * and is useful for fast startup. If snapshot data is provided, the + * isolate will be started using that snapshot data. Requires a core snapshot or + * an app snapshot created by Dart_CreateSnapshot or + * Dart_CreatePrecompiledSnapshot* from a VM with the same version. + * + * Requires there to be no current isolate. + * + * \param script_uri The main source file or snapshot this isolate will load. + * The VM will provide this URI to the Dart_IsolateGroupCreateCallback when a child + * isolate is created by Isolate.spawn. The embedder should use a URI that + * allows it to load the same program into such a child isolate. + * \param name A short name for the isolate to improve debugging messages. + * Typically of the format 'foo.dart:main()'. + * \param isolate_snapshot_data + * \param isolate_snapshot_instructions Buffers containing a snapshot of the + * isolate or NULL if no snapshot is provided. If provided, the buffers must + * remain valid until the isolate shuts down. + * \param flags Pointer to VM specific flags or NULL for default flags. + * \param isolate_group_data Embedder group data. This data can be obtained + * by calling Dart_IsolateGroupData and will be passed to the + * Dart_IsolateShutdownCallback, Dart_IsolateCleanupCallback, and + * Dart_IsolateGroupCleanupCallback. + * \param isolate_data Embedder data. This data will be passed to + * the Dart_IsolateGroupCreateCallback when new isolates are spawned from + * this parent isolate. + * \param error Returns NULL if creation is successful, an error message + * otherwise. The caller is responsible for calling free() on the error + * message. + * + * \return The new isolate on success, or NULL if isolate creation failed. + */ +DART_EXPORT Dart_Isolate +Dart_CreateIsolateGroup(const char* script_uri, + const char* name, + const uint8_t* isolate_snapshot_data, + const uint8_t* isolate_snapshot_instructions, + Dart_IsolateFlags* flags, + void* isolate_group_data, + void* isolate_data, + char** error); +/* TODO(turnidge): Document behavior when there is already a current + * isolate. */ + +/** + * Creates a new isolate from a Dart Kernel file. The new isolate + * becomes the current isolate. + * + * Requires there to be no current isolate. + * + * \param script_uri The main source file or snapshot this isolate will load. + * The VM will provide this URI to the Dart_IsolateGroupCreateCallback when a child + * isolate is created by Isolate.spawn. The embedder should use a URI that + * allows it to load the same program into such a child isolate. + * \param name A short name for the isolate to improve debugging messages. + * Typically of the format 'foo.dart:main()'. + * \param kernel_buffer + * \param kernel_buffer_size A buffer which contains a kernel/DIL program. Must + * remain valid until isolate shutdown. + * \param flags Pointer to VM specific flags or NULL for default flags. + * \param isolate_group_data Embedder group data. This data can be obtained + * by calling Dart_IsolateGroupData and will be passed to the + * Dart_IsolateShutdownCallback, Dart_IsolateCleanupCallback, and + * Dart_IsolateGroupCleanupCallback. + * \param isolate_data Embedder data. This data will be passed to + * the Dart_IsolateGroupCreateCallback when new isolates are spawned from + * this parent isolate. + * \param error Returns NULL if creation is successful, an error message + * otherwise. The caller is responsible for calling free() on the error + * message. + * + * \return The new isolate on success, or NULL if isolate creation failed. + */ +DART_EXPORT Dart_Isolate +Dart_CreateIsolateGroupFromKernel(const char* script_uri, + const char* name, + const uint8_t* kernel_buffer, + intptr_t kernel_buffer_size, + Dart_IsolateFlags* flags, + void* isolate_group_data, + void* isolate_data, + char** error); +/** + * Shuts down the current isolate. After this call, the current isolate is NULL. + * Any current scopes created by Dart_EnterScope will be exited. Invokes the + * shutdown callback and any callbacks of remaining weak persistent handles. + * + * Requires there to be a current isolate. + */ +DART_EXPORT void Dart_ShutdownIsolate(); +/* TODO(turnidge): Document behavior when there is no current isolate. */ + +/** + * Returns the current isolate. Will return NULL if there is no + * current isolate. + */ +DART_EXPORT Dart_Isolate Dart_CurrentIsolate(); + +/** + * Returns the callback data associated with the current isolate. This + * data was set when the isolate got created or initialized. + */ +DART_EXPORT void* Dart_CurrentIsolateData(); + +/** + * Returns the callback data associated with the given isolate. This + * data was set when the isolate got created or initialized. + */ +DART_EXPORT void* Dart_IsolateData(Dart_Isolate isolate); + +/** + * Returns the current isolate group. Will return NULL if there is no + * current isolate group. + */ +DART_EXPORT Dart_IsolateGroup Dart_CurrentIsolateGroup(); + +/** + * Returns the callback data associated with the current isolate group. This + * data was passed to the isolate group when it was created. + */ +DART_EXPORT void* Dart_CurrentIsolateGroupData(); + +/** + * Returns the callback data associated with the specified isolate group. This + * data was passed to the isolate when it was created. + * The embedder is responsible for ensuring the consistency of this data + * with respect to the lifecycle of an isolate group. + */ +DART_EXPORT void* Dart_IsolateGroupData(Dart_Isolate isolate); + +/** + * Returns the debugging name for the current isolate. + * + * This name is unique to each isolate and should only be used to make + * debugging messages more comprehensible. + */ +DART_EXPORT Dart_Handle Dart_DebugName(); + +/** + * Returns the ID for an isolate which is used to query the service protocol. + * + * It is the responsibility of the caller to free the returned ID. + */ +DART_EXPORT const char* Dart_IsolateServiceId(Dart_Isolate isolate); + +/** + * Enters an isolate. After calling this function, + * the current isolate will be set to the provided isolate. + * + * Requires there to be no current isolate. Multiple threads may not be in + * the same isolate at once. + */ +DART_EXPORT void Dart_EnterIsolate(Dart_Isolate isolate); + +/** + * Kills the given isolate. + * + * This function has the same effect as dart:isolate's + * Isolate.kill(priority:immediate). + * It can interrupt ordinary Dart code but not native code. If the isolate is + * in the middle of a long running native function, the isolate will not be + * killed until control returns to Dart. + * + * Does not require a current isolate. It is safe to kill the current isolate if + * there is one. + */ +DART_EXPORT void Dart_KillIsolate(Dart_Isolate isolate); + +/** + * Notifies the VM that the embedder expects |size| bytes of memory have become + * unreachable. The VM may use this hint to adjust the garbage collector's + * growth policy. + * + * Multiple calls are interpreted as increasing, not replacing, the estimate of + * unreachable memory. + * + * Requires there to be a current isolate. + */ +DART_EXPORT void Dart_HintFreed(intptr_t size); + +/** + * Notifies the VM that the embedder expects to be idle until |deadline|. The VM + * may use this time to perform garbage collection or other tasks to avoid + * delays during execution of Dart code in the future. + * + * |deadline| is measured in microseconds against the system's monotonic time. + * This clock can be accessed via Dart_TimelineGetMicros(). + * + * Requires there to be a current isolate. + */ +DART_EXPORT void Dart_NotifyIdle(int64_t deadline); + +/** + * Notifies the VM that the system is running low on memory. + * + * Does not require a current isolate. Only valid after calling Dart_Initialize. + */ +DART_EXPORT void Dart_NotifyLowMemory(); + +/** + * Starts the CPU sampling profiler. + */ +DART_EXPORT void Dart_StartProfiling(); + +/** + * Stops the CPU sampling profiler. + * + * Note that some profile samples might still be taken after this fucntion + * returns due to the asynchronous nature of the implementation on some + * platforms. + */ +DART_EXPORT void Dart_StopProfiling(); + +/** + * Notifies the VM that the current thread should not be profiled until a + * matching call to Dart_ThreadEnableProfiling is made. + * + * NOTE: By default, if a thread has entered an isolate it will be profiled. + * This function should be used when an embedder knows a thread is about + * to make a blocking call and wants to avoid unnecessary interrupts by + * the profiler. + */ +DART_EXPORT void Dart_ThreadDisableProfiling(); + +/** + * Notifies the VM that the current thread should be profiled. + * + * NOTE: It is only legal to call this function *after* calling + * Dart_ThreadDisableProfiling. + * + * NOTE: By default, if a thread has entered an isolate it will be profiled. + */ +DART_EXPORT void Dart_ThreadEnableProfiling(); + +/** + * Register symbol information for the Dart VM's profiler and crash dumps. + * + * This consumes the output of //topaz/runtime/dart/profiler_symbols, which + * should be treated as opaque. + */ +DART_EXPORT void Dart_AddSymbols(const char* dso_name, + void* buffer, + intptr_t buffer_size); + +/** + * Exits an isolate. After this call, Dart_CurrentIsolate will + * return NULL. + * + * Requires there to be a current isolate. + */ +DART_EXPORT void Dart_ExitIsolate(); +/* TODO(turnidge): We don't want users of the api to be able to exit a + * "pure" dart isolate. Implement and document. */ + +/** + * Creates a full snapshot of the current isolate heap. + * + * A full snapshot is a compact representation of the dart vm isolate heap + * and dart isolate heap states. These snapshots are used to initialize + * the vm isolate on startup and fast initialization of an isolate. + * A Snapshot of the heap is created before any dart code has executed. + * + * Requires there to be a current isolate. Not available in the precompiled + * runtime (check Dart_IsPrecompiledRuntime). + * + * \param buffer Returns a pointer to a buffer containing the + * snapshot. This buffer is scope allocated and is only valid + * until the next call to Dart_ExitScope. + * \param size Returns the size of the buffer. + * + * \return A valid handle if no error occurs during the operation. + */ +DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle +Dart_CreateSnapshot(uint8_t** vm_snapshot_data_buffer, + intptr_t* vm_snapshot_data_size, + uint8_t** isolate_snapshot_data_buffer, + intptr_t* isolate_snapshot_data_size); + +/** + * Returns whether the buffer contains a kernel file. + * + * \param buffer Pointer to a buffer that might contain a kernel binary. + * \param buffer_size Size of the buffer. + * + * \return Whether the buffer contains a kernel binary (full or partial). + */ +DART_EXPORT bool Dart_IsKernel(const uint8_t* buffer, intptr_t buffer_size); + +/** + * Make isolate runnable. + * + * When isolates are spawned, this function is used to indicate that + * the creation and initialization (including script loading) of the + * isolate is complete and the isolate can start. + * This function expects there to be no current isolate. + * + * \param isolate The isolate to be made runnable. + * + * \return NULL if successful. Returns an error message otherwise. The caller + * is responsible for freeing the error message. + */ +DART_EXPORT DART_WARN_UNUSED_RESULT char* Dart_IsolateMakeRunnable( + Dart_Isolate isolate); + +/* + * ================== + * Messages and Ports + * ================== + */ + +/** + * A port is used to send or receive inter-isolate messages + */ +typedef int64_t Dart_Port; + +/** + * ILLEGAL_PORT is a port number guaranteed never to be associated with a valid + * port. + */ +#define ILLEGAL_PORT ((Dart_Port)0) + +/** + * A message notification callback. + * + * This callback allows the embedder to provide an alternate wakeup + * mechanism for the delivery of inter-isolate messages. It is the + * responsibility of the embedder to call Dart_HandleMessage to + * process the message. + */ +typedef void (*Dart_MessageNotifyCallback)(Dart_Isolate dest_isolate); + +/** + * Allows embedders to provide an alternative wakeup mechanism for the + * delivery of inter-isolate messages. This setting only applies to + * the current isolate. + * + * Most embedders will only call this function once, before isolate + * execution begins. If this function is called after isolate + * execution begins, the embedder is responsible for threading issues. + */ +DART_EXPORT void Dart_SetMessageNotifyCallback( + Dart_MessageNotifyCallback message_notify_callback); +/* TODO(turnidge): Consider moving this to isolate creation so that it + * is impossible to mess up. */ + +/** + * Query the current message notify callback for the isolate. + * + * \return The current message notify callback for the isolate. + */ +DART_EXPORT Dart_MessageNotifyCallback Dart_GetMessageNotifyCallback(); + +/** + * The VM's default message handler supports pausing an isolate before it + * processes the first message and right after the it processes the isolate's + * final message. This can be controlled for all isolates by two VM flags: + * + * `--pause-isolates-on-start` + * `--pause-isolates-on-exit` + * + * Additionally, Dart_SetShouldPauseOnStart and Dart_SetShouldPauseOnExit can be + * used to control this behaviour on a per-isolate basis. + * + * When an embedder is using a Dart_MessageNotifyCallback the embedder + * needs to cooperate with the VM so that the service protocol can report + * accurate information about isolates and so that tools such as debuggers + * work reliably. + * + * The following functions can be used to implement pausing on start and exit. + */ + +/** + * If the VM flag `--pause-isolates-on-start` was passed this will be true. + * + * \return A boolean value indicating if pause on start was requested. + */ +DART_EXPORT bool Dart_ShouldPauseOnStart(); + +/** + * Override the VM flag `--pause-isolates-on-start` for the current isolate. + * + * \param should_pause Should the isolate be paused on start? + * + * NOTE: This must be called before Dart_IsolateMakeRunnable. + */ +DART_EXPORT void Dart_SetShouldPauseOnStart(bool should_pause); + +/** + * Is the current isolate paused on start? + * + * \return A boolean value indicating if the isolate is paused on start. + */ +DART_EXPORT bool Dart_IsPausedOnStart(); + +/** + * Called when the embedder has paused the current isolate on start and when + * the embedder has resumed the isolate. + * + * \param paused Is the isolate paused on start? + */ +DART_EXPORT void Dart_SetPausedOnStart(bool paused); + +/** + * If the VM flag `--pause-isolates-on-exit` was passed this will be true. + * + * \return A boolean value indicating if pause on exit was requested. + */ +DART_EXPORT bool Dart_ShouldPauseOnExit(); + +/** + * Override the VM flag `--pause-isolates-on-exit` for the current isolate. + * + * \param should_pause Should the isolate be paused on exit? + * + */ +DART_EXPORT void Dart_SetShouldPauseOnExit(bool should_pause); + +/** + * Is the current isolate paused on exit? + * + * \return A boolean value indicating if the isolate is paused on exit. + */ +DART_EXPORT bool Dart_IsPausedOnExit(); + +/** + * Called when the embedder has paused the current isolate on exit and when + * the embedder has resumed the isolate. + * + * \param paused Is the isolate paused on exit? + */ +DART_EXPORT void Dart_SetPausedOnExit(bool paused); + +/** + * Called when the embedder has caught a top level unhandled exception error + * in the current isolate. + * + * NOTE: It is illegal to call this twice on the same isolate without first + * clearing the sticky error to null. + * + * \param error The unhandled exception error. + */ +DART_EXPORT void Dart_SetStickyError(Dart_Handle error); + +/** + * Does the current isolate have a sticky error? + */ +DART_EXPORT bool Dart_HasStickyError(); + +/** + * Gets the sticky error for the current isolate. + * + * \return A handle to the sticky error object or null. + */ +DART_EXPORT Dart_Handle Dart_GetStickyError(); + +/** + * Handles the next pending message for the current isolate. + * + * May generate an unhandled exception error. + * + * \return A valid handle if no error occurs during the operation. + */ +DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle Dart_HandleMessage(); + +/** + * Drains the microtask queue, then blocks the calling thread until the current + * isolate recieves a message, then handles all messages. + * + * \param timeout_millis When non-zero, the call returns after the indicated + number of milliseconds even if no message was received. + * \return A valid handle if no error occurs, otherwise an error handle. + */ +DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle +Dart_WaitForEvent(int64_t timeout_millis); + +/** + * Handles any pending messages for the vm service for the current + * isolate. + * + * This function may be used by an embedder at a breakpoint to avoid + * pausing the vm service. + * + * This function can indirectly cause the message notify callback to + * be called. + * + * \return true if the vm service requests the program resume + * execution, false otherwise + */ +DART_EXPORT bool Dart_HandleServiceMessages(); + +/** + * Does the current isolate have pending service messages? + * + * \return true if the isolate has pending service messages, false otherwise. + */ +DART_EXPORT bool Dart_HasServiceMessages(); + +/** + * Processes any incoming messages for the current isolate. + * + * This function may only be used when the embedder has not provided + * an alternate message delivery mechanism with + * Dart_SetMessageCallbacks. It is provided for convenience. + * + * This function waits for incoming messages for the current + * isolate. As new messages arrive, they are handled using + * Dart_HandleMessage. The routine exits when all ports to the + * current isolate are closed. + * + * \return A valid handle if the run loop exited successfully. If an + * exception or other error occurs while processing messages, an + * error handle is returned. + */ +DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle Dart_RunLoop(); +/* TODO(turnidge): Should this be removed from the public api? */ + +/** + * Gets the main port id for the current isolate. + */ +DART_EXPORT Dart_Port Dart_GetMainPortId(); + +/** + * Does the current isolate have live ReceivePorts? + * + * A ReceivePort is live when it has not been closed. + */ +DART_EXPORT bool Dart_HasLivePorts(); + +/** + * Posts a message for some isolate. The message is a serialized + * object. + * + * Requires there to be a current isolate. + * + * \param port The destination port. + * \param object An object from the current isolate. + * + * \return True if the message was posted. + */ +DART_EXPORT bool Dart_Post(Dart_Port port_id, Dart_Handle object); + +/** + * Returns a new SendPort with the provided port id. + * + * \param port_id The destination port. + * + * \return A new SendPort if no errors occurs. Otherwise returns + * an error handle. + */ +DART_EXPORT Dart_Handle Dart_NewSendPort(Dart_Port port_id); + +/** + * Gets the SendPort id for the provided SendPort. + * \param port A SendPort object whose id is desired. + * \param port_id Returns the id of the SendPort. + * \return Success if no error occurs. Otherwise returns + * an error handle. + */ +DART_EXPORT Dart_Handle Dart_SendPortGetId(Dart_Handle port, + Dart_Port* port_id); + +/* + * ====== + * Scopes + * ====== + */ + +/** + * Enters a new scope. + * + * All new local handles will be created in this scope. Additionally, + * some functions may return "scope allocated" memory which is only + * valid within this scope. + * + * Requires there to be a current isolate. + */ +DART_EXPORT void Dart_EnterScope(); + +/** + * Exits a scope. + * + * The previous scope (if any) becomes the current scope. + * + * Requires there to be a current isolate. + */ +DART_EXPORT void Dart_ExitScope(); + +/** + * The Dart VM uses "zone allocation" for temporary structures. Zones + * support very fast allocation of small chunks of memory. The chunks + * cannot be deallocated individually, but instead zones support + * deallocating all chunks in one fast operation. + * + * This function makes it possible for the embedder to allocate + * temporary data in the VMs zone allocator. + * + * Zone allocation is possible: + * 1. when inside a scope where local handles can be allocated + * 2. when processing a message from a native port in a native port + * handler + * + * All the memory allocated this way will be reclaimed either on the + * next call to Dart_ExitScope or when the native port handler exits. + * + * \param size Size of the memory to allocate. + * + * \return A pointer to the allocated memory. NULL if allocation + * failed. Failure might due to is no current VM zone. + */ +DART_EXPORT uint8_t* Dart_ScopeAllocate(intptr_t size); + +/* + * ======= + * Objects + * ======= + */ + +/** + * Returns the null object. + * + * \return A handle to the null object. + */ +DART_EXPORT Dart_Handle Dart_Null(); + +/** + * Is this object null? + */ +DART_EXPORT bool Dart_IsNull(Dart_Handle object); + +/** + * Returns the empty string object. + * + * \return A handle to the empty string object. + */ +DART_EXPORT Dart_Handle Dart_EmptyString(); + +/** + * Returns types that are not classes, and which therefore cannot be looked up + * as library members by Dart_GetType. + * + * \return A handle to the dynamic, void or Never type. + */ +DART_EXPORT Dart_Handle Dart_TypeDynamic(); +DART_EXPORT Dart_Handle Dart_TypeVoid(); +DART_EXPORT Dart_Handle Dart_TypeNever(); + +/** + * Checks if the two objects are equal. + * + * The result of the comparison is returned through the 'equal' + * parameter. The return value itself is used to indicate success or + * failure, not equality. + * + * May generate an unhandled exception error. + * + * \param obj1 An object to be compared. + * \param obj2 An object to be compared. + * \param equal Returns the result of the equality comparison. + * + * \return A valid handle if no error occurs during the comparison. + */ +DART_EXPORT Dart_Handle Dart_ObjectEquals(Dart_Handle obj1, + Dart_Handle obj2, + bool* equal); + +/** + * Is this object an instance of some type? + * + * The result of the test is returned through the 'instanceof' parameter. + * The return value itself is used to indicate success or failure. + * + * \param object An object. + * \param type A type. + * \param instanceof Return true if 'object' is an instance of type 'type'. + * + * \return A valid handle if no error occurs during the operation. + */ +DART_EXPORT Dart_Handle Dart_ObjectIsType(Dart_Handle object, + Dart_Handle type, + bool* instanceof); + +/** + * Query object type. + * + * \param object Some Object. + * + * \return true if Object is of the specified type. + */ +DART_EXPORT bool Dart_IsInstance(Dart_Handle object); +DART_EXPORT bool Dart_IsNumber(Dart_Handle object); +DART_EXPORT bool Dart_IsInteger(Dart_Handle object); +DART_EXPORT bool Dart_IsDouble(Dart_Handle object); +DART_EXPORT bool Dart_IsBoolean(Dart_Handle object); +DART_EXPORT bool Dart_IsString(Dart_Handle object); +DART_EXPORT bool Dart_IsStringLatin1(Dart_Handle object); /* (ISO-8859-1) */ +DART_EXPORT bool Dart_IsExternalString(Dart_Handle object); +DART_EXPORT bool Dart_IsList(Dart_Handle object); +DART_EXPORT bool Dart_IsMap(Dart_Handle object); +DART_EXPORT bool Dart_IsLibrary(Dart_Handle object); +DART_EXPORT bool Dart_IsType(Dart_Handle handle); +DART_EXPORT bool Dart_IsFunction(Dart_Handle handle); +DART_EXPORT bool Dart_IsVariable(Dart_Handle handle); +DART_EXPORT bool Dart_IsTypeVariable(Dart_Handle handle); +DART_EXPORT bool Dart_IsClosure(Dart_Handle object); +DART_EXPORT bool Dart_IsTypedData(Dart_Handle object); +DART_EXPORT bool Dart_IsByteBuffer(Dart_Handle object); +DART_EXPORT bool Dart_IsFuture(Dart_Handle object); + +/* + * ========= + * Instances + * ========= + */ + +/* + * For the purposes of the embedding api, not all objects returned are + * Dart language objects. Within the api, we use the term 'Instance' + * to indicate handles which refer to true Dart language objects. + * + * TODO(turnidge): Reorganize the "Object" section above, pulling down + * any functions that more properly belong here. */ + +/** + * Gets the type of a Dart language object. + * + * \param instance Some Dart object. + * + * \return If no error occurs, the type is returned. Otherwise an + * error handle is returned. + */ +DART_EXPORT Dart_Handle Dart_InstanceGetType(Dart_Handle instance); + +/** + * Returns the name for the provided class type. + * + * \return A valid string handle if no error occurs during the + * operation. + */ +DART_EXPORT Dart_Handle Dart_ClassName(Dart_Handle cls_type); + +/** + * Returns the name for the provided function or method. + * + * \return A valid string handle if no error occurs during the + * operation. + */ +DART_EXPORT Dart_Handle Dart_FunctionName(Dart_Handle function); + +/** + * Returns a handle to the owner of a function. + * + * The owner of an instance method or a static method is its defining + * class. The owner of a top-level function is its defining + * library. The owner of the function of a non-implicit closure is the + * function of the method or closure that defines the non-implicit + * closure. + * + * \return A valid handle to the owner of the function, or an error + * handle if the argument is not a valid handle to a function. + */ +DART_EXPORT Dart_Handle Dart_FunctionOwner(Dart_Handle function); + +/** + * Determines whether a function handle referes to a static function + * of method. + * + * For the purposes of the embedding API, a top-level function is + * implicitly declared static. + * + * \param function A handle to a function or method declaration. + * \param is_static Returns whether the function or method is declared static. + * + * \return A valid handle if no error occurs during the operation. + */ +DART_EXPORT Dart_Handle Dart_FunctionIsStatic(Dart_Handle function, + bool* is_static); + +/** + * Is this object a closure resulting from a tear-off (closurized method)? + * + * Returns true for closures produced when an ordinary method is accessed + * through a getter call. Returns false otherwise, in particular for closures + * produced from local function declarations. + * + * \param object Some Object. + * + * \return true if Object is a tear-off. + */ +DART_EXPORT bool Dart_IsTearOff(Dart_Handle object); + +/** + * Retrieves the function of a closure. + * + * \return A handle to the function of the closure, or an error handle if the + * argument is not a closure. + */ +DART_EXPORT Dart_Handle Dart_ClosureFunction(Dart_Handle closure); + +/** + * Returns a handle to the library which contains class. + * + * \return A valid handle to the library with owns class, null if the class + * has no library or an error handle if the argument is not a valid handle + * to a class type. + */ +DART_EXPORT Dart_Handle Dart_ClassLibrary(Dart_Handle cls_type); + +/* + * ============================= + * Numbers, Integers and Doubles + * ============================= + */ + +/** + * Does this Integer fit into a 64-bit signed integer? + * + * \param integer An integer. + * \param fits Returns true if the integer fits into a 64-bit signed integer. + * + * \return A valid handle if no error occurs during the operation. + */ +DART_EXPORT Dart_Handle Dart_IntegerFitsIntoInt64(Dart_Handle integer, + bool* fits); + +/** + * Does this Integer fit into a 64-bit unsigned integer? + * + * \param integer An integer. + * \param fits Returns true if the integer fits into a 64-bit unsigned integer. + * + * \return A valid handle if no error occurs during the operation. + */ +DART_EXPORT Dart_Handle Dart_IntegerFitsIntoUint64(Dart_Handle integer, + bool* fits); + +/** + * Returns an Integer with the provided value. + * + * \param value The value of the integer. + * + * \return The Integer object if no error occurs. Otherwise returns + * an error handle. + */ +DART_EXPORT Dart_Handle Dart_NewInteger(int64_t value); + +/** + * Returns an Integer with the provided value. + * + * \param value The unsigned value of the integer. + * + * \return The Integer object if no error occurs. Otherwise returns + * an error handle. + */ +DART_EXPORT Dart_Handle Dart_NewIntegerFromUint64(uint64_t value); + +/** + * Returns an Integer with the provided value. + * + * \param value The value of the integer represented as a C string + * containing a hexadecimal number. + * + * \return The Integer object if no error occurs. Otherwise returns + * an error handle. + */ +DART_EXPORT Dart_Handle Dart_NewIntegerFromHexCString(const char* value); + +/** + * Gets the value of an Integer. + * + * The integer must fit into a 64-bit signed integer, otherwise an error occurs. + * + * \param integer An Integer. + * \param value Returns the value of the Integer. + * + * \return A valid handle if no error occurs during the operation. + */ +DART_EXPORT Dart_Handle Dart_IntegerToInt64(Dart_Handle integer, + int64_t* value); + +/** + * Gets the value of an Integer. + * + * The integer must fit into a 64-bit unsigned integer, otherwise an + * error occurs. + * + * \param integer An Integer. + * \param value Returns the value of the Integer. + * + * \return A valid handle if no error occurs during the operation. + */ +DART_EXPORT Dart_Handle Dart_IntegerToUint64(Dart_Handle integer, + uint64_t* value); + +/** + * Gets the value of an integer as a hexadecimal C string. + * + * \param integer An Integer. + * \param value Returns the value of the Integer as a hexadecimal C + * string. This C string is scope allocated and is only valid until + * the next call to Dart_ExitScope. + * + * \return A valid handle if no error occurs during the operation. + */ +DART_EXPORT Dart_Handle Dart_IntegerToHexCString(Dart_Handle integer, + const char** value); + +/** + * Returns a Double with the provided value. + * + * \param value A double. + * + * \return The Double object if no error occurs. Otherwise returns + * an error handle. + */ +DART_EXPORT Dart_Handle Dart_NewDouble(double value); + +/** + * Gets the value of a Double + * + * \param double_obj A Double + * \param value Returns the value of the Double. + * + * \return A valid handle if no error occurs during the operation. + */ +DART_EXPORT Dart_Handle Dart_DoubleValue(Dart_Handle double_obj, double* value); + +/** + * Returns a closure of static function 'function_name' in the class 'class_name' + * in the exported namespace of specified 'library'. + * + * \param library Library object + * \param cls_type Type object representing a Class + * \param function_name Name of the static function in the class + * + * \return A valid Dart instance if no error occurs during the operation. + */ +DART_EXPORT Dart_Handle Dart_GetStaticMethodClosure(Dart_Handle library, + Dart_Handle cls_type, + Dart_Handle function_name); + +/* + * ======== + * Booleans + * ======== + */ + +/** + * Returns the True object. + * + * Requires there to be a current isolate. + * + * \return A handle to the True object. + */ +DART_EXPORT Dart_Handle Dart_True(); + +/** + * Returns the False object. + * + * Requires there to be a current isolate. + * + * \return A handle to the False object. + */ +DART_EXPORT Dart_Handle Dart_False(); + +/** + * Returns a Boolean with the provided value. + * + * \param value true or false. + * + * \return The Boolean object if no error occurs. Otherwise returns + * an error handle. + */ +DART_EXPORT Dart_Handle Dart_NewBoolean(bool value); + +/** + * Gets the value of a Boolean + * + * \param boolean_obj A Boolean + * \param value Returns the value of the Boolean. + * + * \return A valid handle if no error occurs during the operation. + */ +DART_EXPORT Dart_Handle Dart_BooleanValue(Dart_Handle boolean_obj, bool* value); + +/* + * ======= + * Strings + * ======= + */ + +/** + * Gets the length of a String. + * + * \param str A String. + * \param length Returns the length of the String. + * + * \return A valid handle if no error occurs during the operation. + */ +DART_EXPORT Dart_Handle Dart_StringLength(Dart_Handle str, intptr_t* length); + +/** + * Returns a String built from the provided C string + * (There is an implicit assumption that the C string passed in contains + * UTF-8 encoded characters and '\0' is considered as a termination + * character). + * + * \param value A C String + * + * \return The String object if no error occurs. Otherwise returns + * an error handle. + */ +DART_EXPORT Dart_Handle Dart_NewStringFromCString(const char* str); +/* TODO(turnidge): Document what happens when we run out of memory + * during this call. */ + +/** + * Returns a String built from an array of UTF-8 encoded characters. + * + * \param utf8_array An array of UTF-8 encoded characters. + * \param length The length of the codepoints array. + * + * \return The String object if no error occurs. Otherwise returns + * an error handle. + */ +DART_EXPORT Dart_Handle Dart_NewStringFromUTF8(const uint8_t* utf8_array, + intptr_t length); + +/** + * Returns a String built from an array of UTF-16 encoded characters. + * + * \param utf16_array An array of UTF-16 encoded characters. + * \param length The length of the codepoints array. + * + * \return The String object if no error occurs. Otherwise returns + * an error handle. + */ +DART_EXPORT Dart_Handle Dart_NewStringFromUTF16(const uint16_t* utf16_array, + intptr_t length); + +/** + * Returns a String built from an array of UTF-32 encoded characters. + * + * \param utf32_array An array of UTF-32 encoded characters. + * \param length The length of the codepoints array. + * + * \return The String object if no error occurs. Otherwise returns + * an error handle. + */ +DART_EXPORT Dart_Handle Dart_NewStringFromUTF32(const int32_t* utf32_array, + intptr_t length); + +/** + * Returns a String which references an external array of + * Latin-1 (ISO-8859-1) encoded characters. + * + * \param latin1_array Array of Latin-1 encoded characters. This must not move. + * \param length The length of the characters array. + * \param peer An external pointer to associate with this string. + * \param external_allocation_size The number of externally allocated + * bytes for peer. Used to inform the garbage collector. + * \param callback A callback to be called when this string is finalized. + * + * \return The String object if no error occurs. Otherwise returns + * an error handle. + */ +DART_EXPORT Dart_Handle +Dart_NewExternalLatin1String(const uint8_t* latin1_array, + intptr_t length, + void* peer, + intptr_t external_allocation_size, + Dart_WeakPersistentHandleFinalizer callback); + +/** + * Returns a String which references an external array of UTF-16 encoded + * characters. + * + * \param utf16_array An array of UTF-16 encoded characters. This must not move. + * \param length The length of the characters array. + * \param peer An external pointer to associate with this string. + * \param external_allocation_size The number of externally allocated + * bytes for peer. Used to inform the garbage collector. + * \param callback A callback to be called when this string is finalized. + * + * \return The String object if no error occurs. Otherwise returns + * an error handle. + */ +DART_EXPORT Dart_Handle +Dart_NewExternalUTF16String(const uint16_t* utf16_array, + intptr_t length, + void* peer, + intptr_t external_allocation_size, + Dart_WeakPersistentHandleFinalizer callback); + +/** + * Gets the C string representation of a String. + * (It is a sequence of UTF-8 encoded values with a '\0' termination.) + * + * \param str A string. + * \param cstr Returns the String represented as a C string. + * This C string is scope allocated and is only valid until + * the next call to Dart_ExitScope. + * + * \return A valid handle if no error occurs during the operation. + */ +DART_EXPORT Dart_Handle Dart_StringToCString(Dart_Handle str, + const char** cstr); + +/** + * Gets a UTF-8 encoded representation of a String. + * + * Any unpaired surrogate code points in the string will be converted as + * replacement characters (U+FFFD, 0xEF 0xBF 0xBD in UTF-8). If you need + * to preserve unpaired surrogates, use the Dart_StringToUTF16 function. + * + * \param str A string. + * \param utf8_array Returns the String represented as UTF-8 code + * units. This UTF-8 array is scope allocated and is only valid + * until the next call to Dart_ExitScope. + * \param length Used to return the length of the array which was + * actually used. + * + * \return A valid handle if no error occurs during the operation. + */ +DART_EXPORT Dart_Handle Dart_StringToUTF8(Dart_Handle str, + uint8_t** utf8_array, + intptr_t* length); + +/** + * Gets the data corresponding to the string object. This function returns + * the data only for Latin-1 (ISO-8859-1) string objects. For all other + * string objects it returns an error. + * + * \param str A string. + * \param latin1_array An array allocated by the caller, used to return + * the string data. + * \param length Used to pass in the length of the provided array. + * Used to return the length of the array which was actually used. + * + * \return A valid handle if no error occurs during the operation. + */ +DART_EXPORT Dart_Handle Dart_StringToLatin1(Dart_Handle str, + uint8_t* latin1_array, + intptr_t* length); + +/** + * Gets the UTF-16 encoded representation of a string. + * + * \param str A string. + * \param utf16_array An array allocated by the caller, used to return + * the array of UTF-16 encoded characters. + * \param length Used to pass in the length of the provided array. + * Used to return the length of the array which was actually used. + * + * \return A valid handle if no error occurs during the operation. + */ +DART_EXPORT Dart_Handle Dart_StringToUTF16(Dart_Handle str, + uint16_t* utf16_array, + intptr_t* length); + +/** + * Gets the storage size in bytes of a String. + * + * \param str A String. + * \param length Returns the storage size in bytes of the String. + * This is the size in bytes needed to store the String. + * + * \return A valid handle if no error occurs during the operation. + */ +DART_EXPORT Dart_Handle Dart_StringStorageSize(Dart_Handle str, intptr_t* size); + +/** + * Retrieves some properties associated with a String. + * Properties retrieved are: + * - character size of the string (one or two byte) + * - length of the string + * - peer pointer of string if it is an external string. + * \param str A String. + * \param char_size Returns the character size of the String. + * \param str_len Returns the length of the String. + * \param peer Returns the peer pointer associated with the String or 0 if + * there is no peer pointer for it. + * \return Success if no error occurs. Otherwise returns + * an error handle. + */ +DART_EXPORT Dart_Handle Dart_StringGetProperties(Dart_Handle str, + intptr_t* char_size, + intptr_t* str_len, + void** peer); + +/* + * ===== + * Lists + * ===== + */ + +/** + * Returns a List of the desired length. + * + * \param length The length of the list. + * + * \return The List object if no error occurs. Otherwise returns + * an error handle. + */ +DART_EXPORT Dart_Handle Dart_NewList(intptr_t length); + +typedef enum { + Dart_CoreType_Dynamic, + Dart_CoreType_Int, + Dart_CoreType_String, +} Dart_CoreType_Id; + +// TODO(bkonyi): convert this to use nullable types once NNBD is enabled. +/** + * Returns a List of the desired length with the desired legacy element type. + * + * \param element_type_id The type of elements of the list. + * \param length The length of the list. + * + * \return The List object if no error occurs. Otherwise returns an error + * handle. + */ +DART_EXPORT Dart_Handle Dart_NewListOf(Dart_CoreType_Id element_type_id, + intptr_t length); + +/** + * Returns a List of the desired length with the desired element type. + * + * \param element_type Handle to a nullable type object. E.g., from + * Dart_GetType or Dart_GetNullableType. + * + * \param length The length of the list. + * + * \return The List object if no error occurs. Otherwise returns + * an error handle. + */ +DART_EXPORT Dart_Handle Dart_NewListOfType(Dart_Handle element_type, + intptr_t length); + +/** + * Returns a List of the desired length with the desired element type, filled + * with the provided object. + * + * \param element_type Handle to a type object. E.g., from Dart_GetType. + * + * \param fill_object Handle to an object of type 'element_type' that will be + * used to populate the list. This parameter can only be Dart_Null() if the + * length of the list is 0 or 'element_type' is a nullable type. + * + * \param length The length of the list. + * + * \return The List object if no error occurs. Otherwise returns + * an error handle. + */ +DART_EXPORT Dart_Handle Dart_NewListOfTypeFilled(Dart_Handle element_type, + Dart_Handle fill_object, + intptr_t length); + +/** + * Gets the length of a List. + * + * May generate an unhandled exception error. + * + * \param list A List. + * \param length Returns the length of the List. + * + * \return A valid handle if no error occurs during the operation. + */ +DART_EXPORT Dart_Handle Dart_ListLength(Dart_Handle list, intptr_t* length); + +/** + * Gets the Object at some index of a List. + * + * If the index is out of bounds, an error occurs. + * + * May generate an unhandled exception error. + * + * \param list A List. + * \param index A valid index into the List. + * + * \return The Object in the List at the specified index if no error + * occurs. Otherwise returns an error handle. + */ +DART_EXPORT Dart_Handle Dart_ListGetAt(Dart_Handle list, intptr_t index); + +/** +* Gets a range of Objects from a List. +* +* If any of the requested index values are out of bounds, an error occurs. +* +* May generate an unhandled exception error. +* +* \param list A List. +* \param offset The offset of the first item to get. +* \param length The number of items to get. +* \param result A pointer to fill with the objects. +* +* \return Success if no error occurs during the operation. +*/ +DART_EXPORT Dart_Handle Dart_ListGetRange(Dart_Handle list, + intptr_t offset, + intptr_t length, + Dart_Handle* result); + +/** + * Sets the Object at some index of a List. + * + * If the index is out of bounds, an error occurs. + * + * May generate an unhandled exception error. + * + * \param array A List. + * \param index A valid index into the List. + * \param value The Object to put in the List. + * + * \return A valid handle if no error occurs during the operation. + */ +DART_EXPORT Dart_Handle Dart_ListSetAt(Dart_Handle list, + intptr_t index, + Dart_Handle value); + +/** + * May generate an unhandled exception error. + */ +DART_EXPORT Dart_Handle Dart_ListGetAsBytes(Dart_Handle list, + intptr_t offset, + uint8_t* native_array, + intptr_t length); + +/** + * May generate an unhandled exception error. + */ +DART_EXPORT Dart_Handle Dart_ListSetAsBytes(Dart_Handle list, + intptr_t offset, + const uint8_t* native_array, + intptr_t length); + +/* + * ==== + * Maps + * ==== + */ + +/** + * Gets the Object at some key of a Map. + * + * May generate an unhandled exception error. + * + * \param map A Map. + * \param key An Object. + * + * \return The value in the map at the specified key, null if the map does not + * contain the key, or an error handle. + */ +DART_EXPORT Dart_Handle Dart_MapGetAt(Dart_Handle map, Dart_Handle key); + +/** + * Returns whether the Map contains a given key. + * + * May generate an unhandled exception error. + * + * \param map A Map. + * + * \return A handle on a boolean indicating whether map contains the key. + * Otherwise returns an error handle. + */ +DART_EXPORT Dart_Handle Dart_MapContainsKey(Dart_Handle map, Dart_Handle key); + +/** + * Gets the list of keys of a Map. + * + * May generate an unhandled exception error. + * + * \param map A Map. + * + * \return The list of key Objects if no error occurs. Otherwise returns an + * error handle. + */ +DART_EXPORT Dart_Handle Dart_MapKeys(Dart_Handle map); + +/* + * ========== + * Typed Data + * ========== + */ + +typedef enum { + Dart_TypedData_kByteData = 0, + Dart_TypedData_kInt8, + Dart_TypedData_kUint8, + Dart_TypedData_kUint8Clamped, + Dart_TypedData_kInt16, + Dart_TypedData_kUint16, + Dart_TypedData_kInt32, + Dart_TypedData_kUint32, + Dart_TypedData_kInt64, + Dart_TypedData_kUint64, + Dart_TypedData_kFloat32, + Dart_TypedData_kFloat64, + Dart_TypedData_kInt32x4, + Dart_TypedData_kFloat32x4, + Dart_TypedData_kFloat64x2, + Dart_TypedData_kInvalid +} Dart_TypedData_Type; + +/** + * Return type if this object is a TypedData object. + * + * \return kInvalid if the object is not a TypedData object or the appropriate + * Dart_TypedData_Type. + */ +DART_EXPORT Dart_TypedData_Type Dart_GetTypeOfTypedData(Dart_Handle object); + +/** + * Return type if this object is an external TypedData object. + * + * \return kInvalid if the object is not an external TypedData object or + * the appropriate Dart_TypedData_Type. + */ +DART_EXPORT Dart_TypedData_Type +Dart_GetTypeOfExternalTypedData(Dart_Handle object); + +/** + * Returns a TypedData object of the desired length and type. + * + * \param type The type of the TypedData object. + * \param length The length of the TypedData object (length in type units). + * + * \return The TypedData object if no error occurs. Otherwise returns + * an error handle. + */ +DART_EXPORT Dart_Handle Dart_NewTypedData(Dart_TypedData_Type type, + intptr_t length); + +/** + * Returns a TypedData object which references an external data array. + * + * \param type The type of the data array. + * \param data A data array. This array must not move. + * \param length The length of the data array (length in type units). + * + * \return The TypedData object if no error occurs. Otherwise returns + * an error handle. + */ +DART_EXPORT Dart_Handle Dart_NewExternalTypedData(Dart_TypedData_Type type, + void* data, + intptr_t length); + +/** + * Returns a TypedData object which references an external data array. + * + * \param type The type of the data array. + * \param data A data array. This array must not move. + * \param length The length of the data array (length in type units). + * \param peer A pointer to a native object or NULL. This value is + * provided to callback when it is invoked. + * \param external_allocation_size The number of externally allocated + * bytes for peer. Used to inform the garbage collector. + * \param callback A function pointer that will be invoked sometime + * after the object is garbage collected, unless the handle has been deleted. + * A valid callback needs to be specified it cannot be NULL. + * + * \return The TypedData object if no error occurs. Otherwise returns + * an error handle. + */ +DART_EXPORT Dart_Handle Dart_NewExternalTypedDataWithFinalizer( + Dart_TypedData_Type type, + void* data, + intptr_t length, + void* peer, + intptr_t external_allocation_size, + Dart_WeakPersistentHandleFinalizer callback); + +/** + * Returns a ByteBuffer object for the typed data. + * + * \param type_data The TypedData object. + * + * \return The ByteBuffer object if no error occurs. Otherwise returns + * an error handle. + */ +DART_EXPORT Dart_Handle Dart_NewByteBuffer(Dart_Handle typed_data); + +/** + * Acquires access to the internal data address of a TypedData object. + * + * \param object The typed data object whose internal data address is to + * be accessed. + * \param type The type of the object is returned here. + * \param data The internal data address is returned here. + * \param len Size of the typed array is returned here. + * + * Notes: + * When the internal address of the object is acquired any calls to a + * Dart API function that could potentially allocate an object or run + * any Dart code will return an error. + * + * Any Dart API functions for accessing the data should not be called + * before the corresponding release. In particular, the object should + * not be acquired again before its release. This leads to undefined + * behavior. + * + * \return Success if the internal data address is acquired successfully. + * Otherwise, returns an error handle. + */ +DART_EXPORT Dart_Handle Dart_TypedDataAcquireData(Dart_Handle object, + Dart_TypedData_Type* type, + void** data, + intptr_t* len); + +/** + * Releases access to the internal data address that was acquired earlier using + * Dart_TypedDataAcquireData. + * + * \param object The typed data object whose internal data address is to be + * released. + * + * \return Success if the internal data address is released successfully. + * Otherwise, returns an error handle. + */ +DART_EXPORT Dart_Handle Dart_TypedDataReleaseData(Dart_Handle object); + +/** + * Returns the TypedData object associated with the ByteBuffer object. + * + * \param byte_buffer The ByteBuffer object. + * + * \return The TypedData object if no error occurs. Otherwise returns + * an error handle. + */ +DART_EXPORT Dart_Handle Dart_GetDataFromByteBuffer(Dart_Handle byte_buffer); + +/* + * ============================================================ + * Invoking Constructors, Methods, Closures and Field accessors + * ============================================================ + */ + +/** + * Invokes a constructor, creating a new object. + * + * This function allows hidden constructors (constructors with leading + * underscores) to be called. + * + * \param type Type of object to be constructed. + * \param constructor_name The name of the constructor to invoke. Use + * Dart_Null() or Dart_EmptyString() to invoke the unnamed constructor. + * This name should not include the name of the class. + * \param number_of_arguments Size of the arguments array. + * \param arguments An array of arguments to the constructor. + * + * \return If the constructor is called and completes successfully, + * then the new object. If an error occurs during execution, then an + * error handle is returned. + */ +DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle +Dart_New(Dart_Handle type, + Dart_Handle constructor_name, + int number_of_arguments, + Dart_Handle* arguments); + +/** + * Allocate a new object without invoking a constructor. + * + * \param type The type of an object to be allocated. + * + * \return The new object. If an error occurs during execution, then an + * error handle is returned. + */ +DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle Dart_Allocate(Dart_Handle type); + +/** + * Allocate a new object without invoking a constructor, and sets specified + * native fields. + * + * \param type The type of an object to be allocated. + * \param num_native_fields The number of native fields to set. + * \param native_fields An array containing the value of native fields. + * + * \return The new object. If an error occurs during execution, then an + * error handle is returned. + */ +DART_EXPORT Dart_Handle +Dart_AllocateWithNativeFields(Dart_Handle type, + intptr_t num_native_fields, + const intptr_t* native_fields); + +/** + * Invokes a method or function. + * + * The 'target' parameter may be an object, type, or library. If + * 'target' is an object, then this function will invoke an instance + * method. If 'target' is a type, then this function will invoke a + * static method. If 'target' is a library, then this function will + * invoke a top-level function from that library. + * NOTE: This API call cannot be used to invoke methods of a type object. + * + * This function ignores visibility (leading underscores in names). + * + * May generate an unhandled exception error. + * + * \param target An object, type, or library. + * \param name The name of the function or method to invoke. + * \param number_of_arguments Size of the arguments array. + * \param arguments An array of arguments to the function. + * + * \return If the function or method is called and completes + * successfully, then the return value is returned. If an error + * occurs during execution, then an error handle is returned. + */ +DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle +Dart_Invoke(Dart_Handle target, + Dart_Handle name, + int number_of_arguments, + Dart_Handle* arguments); +/* TODO(turnidge): Document how to invoke operators. */ + +/** + * Invokes a Closure with the given arguments. + * + * May generate an unhandled exception error. + * + * \return If no error occurs during execution, then the result of + * invoking the closure is returned. If an error occurs during + * execution, then an error handle is returned. + */ +DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle +Dart_InvokeClosure(Dart_Handle closure, + int number_of_arguments, + Dart_Handle* arguments); + +/** + * Invokes a Generative Constructor on an object that was previously + * allocated using Dart_Allocate/Dart_AllocateWithNativeFields. + * + * The 'target' parameter must be an object. + * + * This function ignores visibility (leading underscores in names). + * + * May generate an unhandled exception error. + * + * \param target An object. + * \param name The name of the constructor to invoke. + * Use Dart_Null() or Dart_EmptyString() to invoke the unnamed constructor. + * \param number_of_arguments Size of the arguments array. + * \param arguments An array of arguments to the function. + * + * \return If the constructor is called and completes + * successfully, then the object is returned. If an error + * occurs during execution, then an error handle is returned. + */ +DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle +Dart_InvokeConstructor(Dart_Handle object, + Dart_Handle name, + int number_of_arguments, + Dart_Handle* arguments); + +/** + * Gets the value of a field. + * + * The 'container' parameter may be an object, type, or library. If + * 'container' is an object, then this function will access an + * instance field. If 'container' is a type, then this function will + * access a static field. If 'container' is a library, then this + * function will access a top-level variable. + * NOTE: This API call cannot be used to access fields of a type object. + * + * This function ignores field visibility (leading underscores in names). + * + * May generate an unhandled exception error. + * + * \param container An object, type, or library. + * \param name A field name. + * + * \return If no error occurs, then the value of the field is + * returned. Otherwise an error handle is returned. + */ +DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle +Dart_GetField(Dart_Handle container, Dart_Handle name); + +/** + * Sets the value of a field. + * + * The 'container' parameter may actually be an object, type, or + * library. If 'container' is an object, then this function will + * access an instance field. If 'container' is a type, then this + * function will access a static field. If 'container' is a library, + * then this function will access a top-level variable. + * NOTE: This API call cannot be used to access fields of a type object. + * + * This function ignores field visibility (leading underscores in names). + * + * May generate an unhandled exception error. + * + * \param container An object, type, or library. + * \param name A field name. + * \param value The new field value. + * + * \return A valid handle if no error occurs. + */ +DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle +Dart_SetField(Dart_Handle container, Dart_Handle name, Dart_Handle value); + +/* + * ========== + * Exceptions + * ========== + */ + +/* + * TODO(turnidge): Remove these functions from the api and replace all + * uses with Dart_NewUnhandledExceptionError. */ + +/** + * Throws an exception. + * + * This function causes a Dart language exception to be thrown. This + * will proceed in the standard way, walking up Dart frames until an + * appropriate 'catch' block is found, executing 'finally' blocks, + * etc. + * + * If an error handle is passed into this function, the error is + * propagated immediately. See Dart_PropagateError for a discussion + * of error propagation. + * + * If successful, this function does not return. Note that this means + * that the destructors of any stack-allocated C++ objects will not be + * called. If there are no Dart frames on the stack, an error occurs. + * + * \return An error handle if the exception was not thrown. + * Otherwise the function does not return. + */ +DART_EXPORT Dart_Handle Dart_ThrowException(Dart_Handle exception); + +/** + * Rethrows an exception. + * + * Rethrows an exception, unwinding all dart frames on the stack. If + * successful, this function does not return. Note that this means + * that the destructors of any stack-allocated C++ objects will not be + * called. If there are no Dart frames on the stack, an error occurs. + * + * \return An error handle if the exception was not thrown. + * Otherwise the function does not return. + */ +DART_EXPORT Dart_Handle Dart_ReThrowException(Dart_Handle exception, + Dart_Handle stacktrace); + +/* + * =========================== + * Native fields and functions + * =========================== + */ + +/** + * Gets the number of native instance fields in an object. + */ +DART_EXPORT Dart_Handle Dart_GetNativeInstanceFieldCount(Dart_Handle obj, + int* count); + +/** + * Gets the value of a native field. + * + * TODO(turnidge): Document. + */ +DART_EXPORT Dart_Handle Dart_GetNativeInstanceField(Dart_Handle obj, + int index, + intptr_t* value); + +/** + * Sets the value of a native field. + * + * TODO(turnidge): Document. + */ +DART_EXPORT Dart_Handle Dart_SetNativeInstanceField(Dart_Handle obj, + int index, + intptr_t value); + +/** + * The arguments to a native function. + * + * This object is passed to a native function to represent its + * arguments and return value. It allows access to the arguments to a + * native function by index. It also allows the return value of a + * native function to be set. + */ +typedef struct _Dart_NativeArguments* Dart_NativeArguments; + +/** + * Extracts current isolate group data from the native arguments structure. + */ +DART_EXPORT void* Dart_GetNativeIsolateGroupData(Dart_NativeArguments args); + +typedef enum { + Dart_NativeArgument_kBool = 0, + Dart_NativeArgument_kInt32, + Dart_NativeArgument_kUint32, + Dart_NativeArgument_kInt64, + Dart_NativeArgument_kUint64, + Dart_NativeArgument_kDouble, + Dart_NativeArgument_kString, + Dart_NativeArgument_kInstance, + Dart_NativeArgument_kNativeFields, +} Dart_NativeArgument_Type; + +typedef struct _Dart_NativeArgument_Descriptor { + uint8_t type; + uint8_t index; +} Dart_NativeArgument_Descriptor; + +typedef union _Dart_NativeArgument_Value { + bool as_bool; + int32_t as_int32; + uint32_t as_uint32; + int64_t as_int64; + uint64_t as_uint64; + double as_double; + struct { + Dart_Handle dart_str; + void* peer; + } as_string; + struct { + intptr_t num_fields; + intptr_t* values; + } as_native_fields; + Dart_Handle as_instance; +} Dart_NativeArgument_Value; + +enum { + kNativeArgNumberPos = 0, + kNativeArgNumberSize = 8, + kNativeArgTypePos = kNativeArgNumberPos + kNativeArgNumberSize, + kNativeArgTypeSize = 8, +}; + +#define BITMASK(size) ((1 << size) - 1) +#define DART_NATIVE_ARG_DESCRIPTOR(type, position) \ + (((type & BITMASK(kNativeArgTypeSize)) << kNativeArgTypePos) | \ + (position & BITMASK(kNativeArgNumberSize))) + +/** + * Gets the native arguments based on the types passed in and populates + * the passed arguments buffer with appropriate native values. + * + * \param args the Native arguments block passed into the native call. + * \param num_arguments length of argument descriptor array and argument + * values array passed in. + * \param arg_descriptors an array that describes the arguments that + * need to be retrieved. For each argument to be retrieved the descriptor + * contains the argument number (0, 1 etc.) and the argument type + * described using Dart_NativeArgument_Type, e.g: + * DART_NATIVE_ARG_DESCRIPTOR(Dart_NativeArgument_kBool, 1) indicates + * that the first argument is to be retrieved and it should be a boolean. + * \param arg_values array into which the native arguments need to be + * extracted into, the array is allocated by the caller (it could be + * stack allocated to avoid the malloc/free performance overhead). + * + * \return Success if all the arguments could be extracted correctly, + * returns an error handle if there were any errors while extracting the + * arguments (mismatched number of arguments, incorrect types, etc.). + */ +DART_EXPORT Dart_Handle +Dart_GetNativeArguments(Dart_NativeArguments args, + int num_arguments, + const Dart_NativeArgument_Descriptor* arg_descriptors, + Dart_NativeArgument_Value* arg_values); + +/** + * Gets the native argument at some index. + */ +DART_EXPORT Dart_Handle Dart_GetNativeArgument(Dart_NativeArguments args, + int index); +/* TODO(turnidge): Specify the behavior of an out-of-bounds access. */ + +/** + * Gets the number of native arguments. + */ +DART_EXPORT int Dart_GetNativeArgumentCount(Dart_NativeArguments args); + +/** + * Gets all the native fields of the native argument at some index. + * \param args Native arguments structure. + * \param arg_index Index of the desired argument in the structure above. + * \param num_fields size of the intptr_t array 'field_values' passed in. + * \param field_values intptr_t array in which native field values are returned. + * \return Success if the native fields where copied in successfully. Otherwise + * returns an error handle. On success the native field values are copied + * into the 'field_values' array, if the argument at 'arg_index' is a + * null object then 0 is copied as the native field values into the + * 'field_values' array. + */ +DART_EXPORT Dart_Handle +Dart_GetNativeFieldsOfArgument(Dart_NativeArguments args, + int arg_index, + int num_fields, + intptr_t* field_values); + +/** + * Gets the native field of the receiver. + */ +DART_EXPORT Dart_Handle Dart_GetNativeReceiver(Dart_NativeArguments args, + intptr_t* value); + +/** + * Gets a string native argument at some index. + * \param args Native arguments structure. + * \param arg_index Index of the desired argument in the structure above. + * \param peer Returns the peer pointer if the string argument has one. + * \return Success if the string argument has a peer, if it does not + * have a peer then the String object is returned. Otherwise returns + * an error handle (argument is not a String object). + */ +DART_EXPORT Dart_Handle Dart_GetNativeStringArgument(Dart_NativeArguments args, + int arg_index, + void** peer); + +/** + * Gets an integer native argument at some index. + * \param args Native arguments structure. + * \param arg_index Index of the desired argument in the structure above. + * \param value Returns the integer value if the argument is an Integer. + * \return Success if no error occurs. Otherwise returns an error handle. + */ +DART_EXPORT Dart_Handle Dart_GetNativeIntegerArgument(Dart_NativeArguments args, + int index, + int64_t* value); + +/** + * Gets a boolean native argument at some index. + * \param args Native arguments structure. + * \param arg_index Index of the desired argument in the structure above. + * \param value Returns the boolean value if the argument is a Boolean. + * \return Success if no error occurs. Otherwise returns an error handle. + */ +DART_EXPORT Dart_Handle Dart_GetNativeBooleanArgument(Dart_NativeArguments args, + int index, + bool* value); + +/** + * Gets a double native argument at some index. + * \param args Native arguments structure. + * \param arg_index Index of the desired argument in the structure above. + * \param value Returns the double value if the argument is a double. + * \return Success if no error occurs. Otherwise returns an error handle. + */ +DART_EXPORT Dart_Handle Dart_GetNativeDoubleArgument(Dart_NativeArguments args, + int index, + double* value); + +/** + * Sets the return value for a native function. + * + * If retval is an Error handle, then error will be propagated once + * the native functions exits. See Dart_PropagateError for a + * discussion of how different types of errors are propagated. + */ +DART_EXPORT void Dart_SetReturnValue(Dart_NativeArguments args, + Dart_Handle retval); + +DART_EXPORT void Dart_SetWeakHandleReturnValue(Dart_NativeArguments args, + Dart_WeakPersistentHandle rval); + +DART_EXPORT void Dart_SetBooleanReturnValue(Dart_NativeArguments args, + bool retval); + +DART_EXPORT void Dart_SetIntegerReturnValue(Dart_NativeArguments args, + int64_t retval); + +DART_EXPORT void Dart_SetDoubleReturnValue(Dart_NativeArguments args, + double retval); + +/** + * A native function. + */ +typedef void (*Dart_NativeFunction)(Dart_NativeArguments arguments); + +/** + * Native entry resolution callback. + * + * For libraries and scripts which have native functions, the embedder + * can provide a native entry resolver. This callback is used to map a + * name/arity to a Dart_NativeFunction. If no function is found, the + * callback should return NULL. + * + * The parameters to the native resolver function are: + * \param name a Dart string which is the name of the native function. + * \param num_of_arguments is the number of arguments expected by the + * native function. + * \param auto_setup_scope is a boolean flag that can be set by the resolver + * to indicate if this function needs a Dart API scope (see Dart_EnterScope/ + * Dart_ExitScope) to be setup automatically by the VM before calling into + * the native function. By default most native functions would require this + * to be true but some light weight native functions which do not call back + * into the VM through the Dart API may not require a Dart scope to be + * setup automatically. + * + * \return A valid Dart_NativeFunction which resolves to a native entry point + * for the native function. + * + * See Dart_SetNativeResolver. + */ +typedef Dart_NativeFunction (*Dart_NativeEntryResolver)(Dart_Handle name, + int num_of_arguments, + bool* auto_setup_scope); +/* TODO(turnidge): Consider renaming to NativeFunctionResolver or + * NativeResolver. */ + +/** + * Native entry symbol lookup callback. + * + * For libraries and scripts which have native functions, the embedder + * can provide a callback for mapping a native entry to a symbol. This callback + * maps a native function entry PC to the native function name. If no native + * entry symbol can be found, the callback should return NULL. + * + * The parameters to the native reverse resolver function are: + * \param nf A Dart_NativeFunction. + * + * \return A const UTF-8 string containing the symbol name or NULL. + * + * See Dart_SetNativeResolver. + */ +typedef const uint8_t* (*Dart_NativeEntrySymbol)(Dart_NativeFunction nf); + +/* + * =========== + * Environment + * =========== + */ + +/** + * An environment lookup callback function. + * + * \param name The name of the value to lookup in the environment. + * + * \return A valid handle to a string if the name exists in the + * current environment or Dart_Null() if not. + */ +typedef Dart_Handle (*Dart_EnvironmentCallback)(Dart_Handle name); + +/** + * Sets the environment callback for the current isolate. This + * callback is used to lookup environment values by name in the + * current environment. This enables the embedder to supply values for + * the const constructors bool.fromEnvironment, int.fromEnvironment + * and String.fromEnvironment. + */ +DART_EXPORT Dart_Handle +Dart_SetEnvironmentCallback(Dart_EnvironmentCallback callback); + +/** + * Sets the callback used to resolve native functions for a library. + * + * \param library A library. + * \param resolver A native entry resolver. + * + * \return A valid handle if the native resolver was set successfully. + */ +DART_EXPORT Dart_Handle +Dart_SetNativeResolver(Dart_Handle library, + Dart_NativeEntryResolver resolver, + Dart_NativeEntrySymbol symbol); +/* TODO(turnidge): Rename to Dart_LibrarySetNativeResolver? */ + +/** + * Returns the callback used to resolve native functions for a library. + * + * \param library A library. + * \param resolver a pointer to a Dart_NativeEntryResolver + * + * \return A valid handle if the library was found. + */ +DART_EXPORT Dart_Handle +Dart_GetNativeResolver(Dart_Handle library, Dart_NativeEntryResolver* resolver); + +/** + * Returns the callback used to resolve native function symbols for a library. + * + * \param library A library. + * \param resolver a pointer to a Dart_NativeEntrySymbol. + * + * \return A valid handle if the library was found. + */ +DART_EXPORT Dart_Handle Dart_GetNativeSymbol(Dart_Handle library, + Dart_NativeEntrySymbol* resolver); + +/* + * ===================== + * Scripts and Libraries + * ===================== + */ + +typedef enum { + Dart_kCanonicalizeUrl = 0, + Dart_kImportTag, + Dart_kKernelTag, + Dart_kImportExtensionTag, +} Dart_LibraryTag; + +/** + * The library tag handler is a multi-purpose callback provided by the + * embedder to the Dart VM. The embedder implements the tag handler to + * provide the ability to load Dart scripts and imports. + * + * -- TAGS -- + * + * Dart_kCanonicalizeUrl + * + * This tag indicates that the embedder should canonicalize 'url' with + * respect to 'library'. For most embedders, the + * Dart_DefaultCanonicalizeUrl function is a sufficient implementation + * of this tag. The return value should be a string holding the + * canonicalized url. + * + * Dart_kImportTag + * + * This tag is used to load a library from IsolateMirror.loadUri. The embedder + * should call Dart_LoadLibraryFromKernel to provide the library to the VM. The + * return value should be an error or library (the result from + * Dart_LoadLibraryFromKernel). + * + * Dart_kKernelTag + * + * This tag is used to load the intermediate file (kernel) generated by + * the Dart front end. This tag is typically used when a 'hot-reload' + * of an application is needed and the VM is 'use dart front end' mode. + * The dart front end typically compiles all the scripts, imports and part + * files into one intermediate file hence we don't use the source/import or + * script tags. The return value should be an error or a TypedData containing + * the kernel bytes. + * + * Dart_kImportExtensionTag + * + * This tag is used to load an external import (shared object file). The + * extension path must have the scheme 'dart-ext:'. + */ +typedef Dart_Handle (*Dart_LibraryTagHandler)( + Dart_LibraryTag tag, + Dart_Handle library_or_package_map_url, + Dart_Handle url); + +/** + * Sets library tag handler for the current isolate. This handler is + * used to handle the various tags encountered while loading libraries + * or scripts in the isolate. + * + * \param handler Handler code to be used for handling the various tags + * encountered while loading libraries or scripts in the isolate. + * + * \return If no error occurs, the handler is set for the isolate. + * Otherwise an error handle is returned. + * + * TODO(turnidge): Document. + */ +DART_EXPORT Dart_Handle +Dart_SetLibraryTagHandler(Dart_LibraryTagHandler handler); + +/** + * Handles deferred loading requests. When this handler is invoked, it should + * eventually load the deferred loading unit with the given id and call + * Dart_DeferredLoadComplete or Dart_DeferredLoadCompleteError. It is + * recommended that the loading occur asynchronously, but it is permitted to + * call Dart_DeferredLoadComplete or Dart_DeferredLoadCompleteError before the + * handler returns. + * + * If an error is returned, it will be propogated through + * `prefix.loadLibrary()`. This is useful for synchronous + * implementations, which must propogate any unwind errors from + * Dart_DeferredLoadComplete or Dart_DeferredLoadComplete. Otherwise the handler + * should return a non-error such as `Dart_Null()`. + */ +typedef Dart_Handle (*Dart_DeferredLoadHandler)(intptr_t loading_unit_id); + +/** + * Sets the deferred load handler for the current isolate. This handler is + * used to handle loading deferred imports in an AppJIT or AppAOT program. + */ +DART_EXPORT Dart_Handle +Dart_SetDeferredLoadHandler(Dart_DeferredLoadHandler handler); + +/** + * Notifies the VM that a deferred load completed successfully. This function + * will eventually cause the corresponding `prefix.loadLibrary()` futures to + * complete. + * + * Requires the current isolate to be the same current isolate during the + * invocation of the Dart_DeferredLoadHandler. + */ +DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle +Dart_DeferredLoadComplete(intptr_t loading_unit_id, + const uint8_t* snapshot_data, + const uint8_t* snapshot_instructions); + +/** + * Notifies the VM that a deferred load failed. This function + * will eventually cause the corresponding `prefix.loadLibrary()` futures to + * complete with an error. + * + * If `transient` is true, future invocations of `prefix.loadLibrary()` will + * trigger new load requests. If false, futures invocation will complete with + * the same error. + * + * Requires the current isolate to be the same current isolate during the + * invocation of the Dart_DeferredLoadHandler. + */ +DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle +Dart_DeferredLoadCompleteError(intptr_t loading_unit_id, + const char* error_message, + bool transient); + +/** + * Canonicalizes a url with respect to some library. + * + * The url is resolved with respect to the library's url and some url + * normalizations are performed. + * + * This canonicalization function should be sufficient for most + * embedders to implement the Dart_kCanonicalizeUrl tag. + * + * \param base_url The base url relative to which the url is + * being resolved. + * \param url The url being resolved and canonicalized. This + * parameter is a string handle. + * + * \return If no error occurs, a String object is returned. Otherwise + * an error handle is returned. + */ +DART_EXPORT Dart_Handle Dart_DefaultCanonicalizeUrl(Dart_Handle base_url, + Dart_Handle url); + +/** + * Loads the root library for the current isolate. + * + * Requires there to be no current root library. + * + * \param buffer A buffer which contains a kernel binary (see + * pkg/kernel/binary.md). Must remain valid until isolate group shutdown. + * \param buffer_size Length of the passed in buffer. + * + * \return A handle to the root library, or an error. + */ +DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle +Dart_LoadScriptFromKernel(const uint8_t* kernel_buffer, intptr_t kernel_size); + +/** + * Gets the library for the root script for the current isolate. + * + * If the root script has not yet been set for the current isolate, + * this function returns Dart_Null(). This function never returns an + * error handle. + * + * \return Returns the root Library for the current isolate or Dart_Null(). + */ +DART_EXPORT Dart_Handle Dart_RootLibrary(); + +/** + * Sets the root library for the current isolate. + * + * \return Returns an error handle if `library` is not a library handle. + */ +DART_EXPORT Dart_Handle Dart_SetRootLibrary(Dart_Handle library); + +/** + * Lookup or instantiate a legacy type by name and type arguments from a + * Library. + * + * \param library The library containing the class or interface. + * \param class_name The class name for the type. + * \param number_of_type_arguments Number of type arguments. + * For non parametric types the number of type arguments would be 0. + * \param type_arguments Pointer to an array of type arguments. + * For non parameteric types a NULL would be passed in for this argument. + * + * \return If no error occurs, the type is returned. + * Otherwise an error handle is returned. + */ +DART_EXPORT Dart_Handle Dart_GetType(Dart_Handle library, + Dart_Handle class_name, + intptr_t number_of_type_arguments, + Dart_Handle* type_arguments); + +/** + * Lookup or instantiate a nullable type by name and type arguments from + * Library. + * + * \param library The library containing the class or interface. + * \param class_name The class name for the type. + * \param number_of_type_arguments Number of type arguments. + * For non parametric types the number of type arguments would be 0. + * \param type_arguments Pointer to an array of type arguments. + * For non parameteric types a NULL would be passed in for this argument. + * + * \return If no error occurs, the type is returned. + * Otherwise an error handle is returned. + */ +DART_EXPORT Dart_Handle Dart_GetNullableType(Dart_Handle library, + Dart_Handle class_name, + intptr_t number_of_type_arguments, + Dart_Handle* type_arguments); + +/** + * Lookup or instantiate a non-nullable type by name and type arguments from + * Library. + * + * \param library The library containing the class or interface. + * \param class_name The class name for the type. + * \param number_of_type_arguments Number of type arguments. + * For non parametric types the number of type arguments would be 0. + * \param type_arguments Pointer to an array of type arguments. + * For non parameteric types a NULL would be passed in for this argument. + * + * \return If no error occurs, the type is returned. + * Otherwise an error handle is returned. + */ +DART_EXPORT Dart_Handle +Dart_GetNonNullableType(Dart_Handle library, + Dart_Handle class_name, + intptr_t number_of_type_arguments, + Dart_Handle* type_arguments); + +/** + * Creates a nullable version of the provided type. + * + * \param type The type to be converted to a nullable type. + * + * \return If no error occurs, a nullable type is returned. + * Otherwise an error handle is returned. + */ +DART_EXPORT Dart_Handle Dart_TypeToNullableType(Dart_Handle type); + +/** + * Creates a non-nullable version of the provided type. + * + * \param type The type to be converted to a non-nullable type. + * + * \return If no error occurs, a non-nullable type is returned. + * Otherwise an error handle is returned. + */ +DART_EXPORT Dart_Handle Dart_TypeToNonNullableType(Dart_Handle type); + +/** + * A type's nullability. + * + * \param type A Dart type. + * \param result An out parameter containing the result of the check. True if + * the type is of the specified nullability, false otherwise. + * + * \return Returns an error handle if type is not of type Type. + */ +DART_EXPORT Dart_Handle Dart_IsNullableType(Dart_Handle type, bool* result); +DART_EXPORT Dart_Handle Dart_IsNonNullableType(Dart_Handle type, bool* result); +DART_EXPORT Dart_Handle Dart_IsLegacyType(Dart_Handle type, bool* result); + +/** + * Lookup a class or interface by name from a Library. + * + * \param library The library containing the class or interface. + * \param class_name The name of the class or interface. + * + * \return If no error occurs, the class or interface is + * returned. Otherwise an error handle is returned. + */ +DART_EXPORT Dart_Handle Dart_GetClass(Dart_Handle library, + Dart_Handle class_name); +/* TODO(asiva): The above method needs to be removed once all uses + * of it are removed from the embedder code. */ + +/** + * Returns an import path to a Library, such as "file:///test.dart" or + * "dart:core". + */ +DART_EXPORT Dart_Handle Dart_LibraryUrl(Dart_Handle library); + +/** + * Returns a URL from which a Library was loaded. + */ +DART_EXPORT Dart_Handle Dart_LibraryResolvedUrl(Dart_Handle library); + +/** + * \return An array of libraries. + */ +DART_EXPORT Dart_Handle Dart_GetLoadedLibraries(); + +DART_EXPORT Dart_Handle Dart_LookupLibrary(Dart_Handle url); +/* TODO(turnidge): Consider returning Dart_Null() when the library is + * not found to distinguish that from a true error case. */ + +/** + * Report an loading error for the library. + * + * \param library The library that failed to load. + * \param error The Dart error instance containing the load error. + * + * \return If the VM handles the error, the return value is + * a null handle. If it doesn't handle the error, the error + * object is returned. + */ +DART_EXPORT Dart_Handle Dart_LibraryHandleError(Dart_Handle library, + Dart_Handle error); + +/** + * Called by the embedder to load a partial program. Does not set the root + * library. + * + * \param buffer A buffer which contains a kernel binary (see + * pkg/kernel/binary.md). Must remain valid until isolate shutdown. + * \param buffer_size Length of the passed in buffer. + * + * \return A handle to the main library of the compilation unit, or an error. + */ +DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle +Dart_LoadLibraryFromKernel(const uint8_t* kernel_buffer, + intptr_t kernel_buffer_size); + +/** + * Returns a flattened list of pairs. The first element in each pair is the + * importing library and and the second element is the imported library for each + * import in the isolate of a library whose URI's scheme is [scheme]. + * + * Requires there to be a current isolate. + * + * \return A handle to a list of flattened pairs of importer-importee. + */ +DART_EXPORT Dart_Handle Dart_GetImportsOfScheme(Dart_Handle scheme); + +/** + * Indicates that all outstanding load requests have been satisfied. + * This finalizes all the new classes loaded and optionally completes + * deferred library futures. + * + * Requires there to be a current isolate. + * + * \param complete_futures Specify true if all deferred library + * futures should be completed, false otherwise. + * + * \return Success if all classes have been finalized and deferred library + * futures are completed. Otherwise, returns an error. + */ +DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle +Dart_FinalizeLoading(bool complete_futures); + +/* + * ===== + * Peers + * ===== + */ + +/** + * The peer field is a lazily allocated field intended for storage of + * an uncommonly used values. Most instances types can have a peer + * field allocated. The exceptions are subtypes of Null, num, and + * bool. + */ + +/** + * Returns the value of peer field of 'object' in 'peer'. + * + * \param object An object. + * \param peer An out parameter that returns the value of the peer + * field. + * + * \return Returns an error if 'object' is a subtype of Null, num, or + * bool. + */ +DART_EXPORT Dart_Handle Dart_GetPeer(Dart_Handle object, void** peer); + +/** + * Sets the value of the peer field of 'object' to the value of + * 'peer'. + * + * \param object An object. + * \param peer A value to store in the peer field. + * + * \return Returns an error if 'object' is a subtype of Null, num, or + * bool. + */ +DART_EXPORT Dart_Handle Dart_SetPeer(Dart_Handle object, void* peer); + +/* + * ====== + * Kernel + * ====== + */ + +/** + * Experimental support for Dart to Kernel parser isolate. + * + * TODO(hausner): Document finalized interface. + * + */ + +// TODO(33433): Remove kernel service from the embedding API. + +typedef enum { + Dart_KernelCompilationStatus_Unknown = -1, + Dart_KernelCompilationStatus_Ok = 0, + Dart_KernelCompilationStatus_Error = 1, + Dart_KernelCompilationStatus_Crash = 2, +} Dart_KernelCompilationStatus; + +typedef struct { + Dart_KernelCompilationStatus status; + bool null_safety; + char* error; + uint8_t* kernel; + intptr_t kernel_size; +} Dart_KernelCompilationResult; + +DART_EXPORT bool Dart_IsKernelIsolate(Dart_Isolate isolate); +DART_EXPORT bool Dart_KernelIsolateIsRunning(); +DART_EXPORT Dart_Port Dart_KernelPort(); + +/** + * Compiles the given `script_uri` to a kernel file. + * + * \param platform_kernel A buffer containing the kernel of the platform (e.g. + * `vm_platform_strong.dill`). The VM does not take ownership of this memory. + * + * \param platform_kernel_size The length of the platform_kernel buffer. + * + * \return Returns the result of the compilation. + * + * On a successful compilation the returned [Dart_KernelCompilationResult] has + * a status of [Dart_KernelCompilationStatus_Ok] and the `kernel`/`kernel_size` + * fields are set. The caller takes ownership of the malloc()ed buffer. + * + * On a failed compilation the `error` might be set describing the reason for + * the failed compilation. The caller takes ownership of the malloc()ed + * error. + * + * Requires there to be a current isolate. + */ +DART_EXPORT Dart_KernelCompilationResult +Dart_CompileToKernel(const char* script_uri, + const uint8_t* platform_kernel, + const intptr_t platform_kernel_size, + bool incremental_compile, + const char* package_config); + +typedef struct { + const char* uri; + const char* source; +} Dart_SourceFile; + +DART_EXPORT Dart_KernelCompilationResult Dart_KernelListDependencies(); + +/** + * Sets the kernel buffer which will be used to load Dart SDK sources + * dynamically at runtime. + * + * \param platform_kernel A buffer containing kernel which has sources for the + * Dart SDK populated. Note: The VM does not take ownership of this memory. + * + * \param platform_kernel_size The length of the platform_kernel buffer. + */ +DART_EXPORT void Dart_SetDartLibrarySourcesKernel( + const uint8_t* platform_kernel, + const intptr_t platform_kernel_size); + +/** + * Detect the null safety opt-in status. + * + * When running from source, it is based on the opt-in status of `script_uri`. + * When running from a kernel buffer, it is based on the mode used when + * generating `kernel_buffer`. + * When running from an appJIT or AOT snapshot, it is based on the mode used + * when generating `snapshot_data`. + * + * \param script_uri Uri of the script that contains the source code + * + * \param package_config Uri of the package configuration file (either in format + * of .packages or .dart_tool/package_config.json) for the null safety + * detection to resolve package imports against. If this parameter is not + * passed the package resolution of the parent isolate should be used. + * + * \param original_working_directory current working directory when the VM + * process was launched, this is used to correctly resolve the path specified + * for package_config. + * + * \param snapshot_data + * + * \param snapshot_instructions Buffers containing a snapshot of the + * isolate or NULL if no snapshot is provided. If provided, the buffers must + * remain valid until the isolate shuts down. + * + * \param kernel_buffer + * + * \param kernel_buffer_size A buffer which contains a kernel/DIL program. Must + * remain valid until isolate shutdown. + * + * \return Returns true if the null safety is opted in by the input being + * run `script_uri`, `snapshot_data` or `kernel_buffer`. + * + */ +DART_EXPORT bool Dart_DetectNullSafety(const char* script_uri, + const char* package_config, + const char* original_working_directory, + const uint8_t* snapshot_data, + const uint8_t* snapshot_instructions, + const uint8_t* kernel_buffer, + intptr_t kernel_buffer_size); + +#define DART_KERNEL_ISOLATE_NAME "kernel-service" + +/* + * ======= + * Service + * ======= + */ + +#define DART_VM_SERVICE_ISOLATE_NAME "vm-service" + +/** + * Returns true if isolate is the service isolate. + * + * \param isolate An isolate + * + * \return Returns true if 'isolate' is the service isolate. + */ +DART_EXPORT bool Dart_IsServiceIsolate(Dart_Isolate isolate); + +/** + * Writes the CPU profile to the timeline as a series of 'instant' events. + * + * Note that this is an expensive operation. + * + * \param main_port The main port of the Isolate whose profile samples to write. + * \param error An optional error, must be free()ed by caller. + * + * \return Returns true if the profile is successfully written and false + * otherwise. + */ +DART_EXPORT bool Dart_WriteProfileToTimeline(Dart_Port main_port, char** error); + +/* + * ==================== + * Compilation Feedback + * ==================== + */ + +/** + * Record all functions which have been compiled in the current isolate. + * + * \param buffer Returns a pointer to a buffer containing the trace. + * This buffer is scope allocated and is only valid until the next call to + * Dart_ExitScope. + * \param size Returns the size of the buffer. + * \return Returns an valid handle upon success. + */ +DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle +Dart_SaveCompilationTrace(uint8_t** buffer, intptr_t* buffer_length); + +/** + * Compile all functions from data from Dart_SaveCompilationTrace. Unlike JIT + * feedback, this data is fuzzy: loading does not need to happen in the exact + * program that was saved, the saver and loader do not need to agree on checked + * mode versus production mode or debug/release/product. + * + * \return Returns an error handle if a compilation error was encountered. + */ +DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle +Dart_LoadCompilationTrace(uint8_t* buffer, intptr_t buffer_length); + +/** + * Record runtime feedback for the current isolate, including type feedback + * and usage counters. + * + * \param buffer Returns a pointer to a buffer containing the trace. + * This buffer is scope allocated and is only valid until the next call to + * Dart_ExitScope. + * \param size Returns the size of the buffer. + * \return Returns an valid handle upon success. + */ +DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle +Dart_SaveTypeFeedback(uint8_t** buffer, intptr_t* buffer_length); + +/** + * Compile functions using data from Dart_SaveTypeFeedback. The data must from a + * VM with the same version and compiler flags. + * + * \return Returns an error handle if a compilation error was encountered or a + * version mismatch is detected. + */ +DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle +Dart_LoadTypeFeedback(uint8_t* buffer, intptr_t buffer_length); + +/* + * ============== + * Precompilation + * ============== + */ + +/** + * Compiles all functions reachable from entry points and marks + * the isolate to disallow future compilation. + * + * Entry points should be specified using `@pragma("vm:entry-point")` + * annotation. + * + * \return An error handle if a compilation error or runtime error running const + * constructors was encountered. + */ +DART_EXPORT Dart_Handle Dart_Precompile(); + +typedef void (*Dart_CreateLoadingUnitCallback)( + void* callback_data, + intptr_t loading_unit_id, + void** write_callback_data, + void** write_debug_callback_data); +typedef void (*Dart_StreamingWriteCallback)(void* callback_data, + const uint8_t* buffer, + intptr_t size); +typedef void (*Dart_StreamingCloseCallback)(void* callback_data); + +DART_EXPORT Dart_Handle Dart_LoadingUnitLibraryUris(intptr_t loading_unit_id); + +// On Darwin systems, 'dlsym' adds an '_' to the beginning of the symbol name. +// Use the '...CSymbol' definitions for resolving through 'dlsym'. The actual +// symbol names in the objects are given by the '...AsmSymbol' definitions. +#if defined(__APPLE__) +#define kSnapshotBuildIdCSymbol "kDartSnapshotBuildId" +#define kVmSnapshotDataCSymbol "kDartVmSnapshotData" +#define kVmSnapshotInstructionsCSymbol "kDartVmSnapshotInstructions" +#define kIsolateSnapshotDataCSymbol "kDartIsolateSnapshotData" +#define kIsolateSnapshotInstructionsCSymbol "kDartIsolateSnapshotInstructions" +#else +#define kSnapshotBuildIdCSymbol "_kDartSnapshotBuildId" +#define kVmSnapshotDataCSymbol "_kDartVmSnapshotData" +#define kVmSnapshotInstructionsCSymbol "_kDartVmSnapshotInstructions" +#define kIsolateSnapshotDataCSymbol "_kDartIsolateSnapshotData" +#define kIsolateSnapshotInstructionsCSymbol "_kDartIsolateSnapshotInstructions" +#endif + +#define kSnapshotBuildIdAsmSymbol "_kDartSnapshotBuildId" +#define kVmSnapshotDataAsmSymbol "_kDartVmSnapshotData" +#define kVmSnapshotInstructionsAsmSymbol "_kDartVmSnapshotInstructions" +#define kIsolateSnapshotDataAsmSymbol "_kDartIsolateSnapshotData" +#define kIsolateSnapshotInstructionsAsmSymbol \ + "_kDartIsolateSnapshotInstructions" + +/** + * Creates a precompiled snapshot. + * - A root library must have been loaded. + * - Dart_Precompile must have been called. + * + * Outputs an assembly file defining the symbols listed in the definitions + * above. + * + * The assembly should be compiled as a static or shared library and linked or + * loaded by the embedder. Running this snapshot requires a VM compiled with + * DART_PRECOMPILED_SNAPSHOT. The kDartVmSnapshotData and + * kDartVmSnapshotInstructions should be passed to Dart_Initialize. The + * kDartIsolateSnapshotData and kDartIsolateSnapshotInstructions should be + * passed to Dart_CreateIsolateGroup. + * + * The callback will be invoked one or more times to provide the assembly code. + * + * If stripped is true, then the assembly code will not include DWARF + * debugging sections. + * + * If debug_callback_data is provided, debug_callback_data will be used with + * the callback to provide separate debugging information. + * + * \return A valid handle if no error occurs during the operation. + */ +DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle +Dart_CreateAppAOTSnapshotAsAssembly(Dart_StreamingWriteCallback callback, + void* callback_data, + bool stripped, + void* debug_callback_data); +DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle +Dart_CreateAppAOTSnapshotAsAssemblies( + Dart_CreateLoadingUnitCallback next_callback, + void* next_callback_data, + bool stripped, + Dart_StreamingWriteCallback write_callback, + Dart_StreamingCloseCallback close_callback); + +/** + * Creates a precompiled snapshot. + * - A root library must have been loaded. + * - Dart_Precompile must have been called. + * + * Outputs an ELF shared library defining the symbols + * - _kDartVmSnapshotData + * - _kDartVmSnapshotInstructions + * - _kDartIsolateSnapshotData + * - _kDartIsolateSnapshotInstructions + * + * The shared library should be dynamically loaded by the embedder. + * Running this snapshot requires a VM compiled with DART_PRECOMPILED_SNAPSHOT. + * The kDartVmSnapshotData and kDartVmSnapshotInstructions should be passed to + * Dart_Initialize. The kDartIsolateSnapshotData and + * kDartIsolateSnapshotInstructions should be passed to Dart_CreateIsolate. + * + * The callback will be invoked one or more times to provide the binary output. + * + * If stripped is true, then the binary output will not include DWARF + * debugging sections. + * + * If debug_callback_data is provided, debug_callback_data will be used with + * the callback to provide separate debugging information. + * + * \return A valid handle if no error occurs during the operation. + */ +DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle +Dart_CreateAppAOTSnapshotAsElf(Dart_StreamingWriteCallback callback, + void* callback_data, + bool stripped, + void* debug_callback_data); +DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle +Dart_CreateAppAOTSnapshotAsElfs(Dart_CreateLoadingUnitCallback next_callback, + void* next_callback_data, + bool stripped, + Dart_StreamingWriteCallback write_callback, + Dart_StreamingCloseCallback close_callback); + +/** + * Like Dart_CreateAppAOTSnapshotAsAssembly, but only includes + * kDartVmSnapshotData and kDartVmSnapshotInstructions. It also does + * not strip DWARF information from the generated assembly or allow for + * separate debug information. + */ +DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle +Dart_CreateVMAOTSnapshotAsAssembly(Dart_StreamingWriteCallback callback, + void* callback_data); + +/** + * Sorts the class-ids in depth first traversal order of the inheritance + * tree. This is a costly operation, but it can make method dispatch + * more efficient and is done before writing snapshots. + * + * \return A valid handle if no error occurs during the operation. + */ +DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle Dart_SortClasses(); + +/** + * Creates a snapshot that caches compiled code and type feedback for faster + * startup and quicker warmup in a subsequent process. + * + * Outputs a snapshot in two pieces. The pieces should be passed to + * Dart_CreateIsolateGroup in a VM using the same VM snapshot pieces used in the + * current VM. The instructions piece must be loaded with read and execute + * permissions; the data piece may be loaded as read-only. + * + * - Requires the VM to have not been started with --precompilation. + * - Not supported when targeting IA32. + * - The VM writing the snapshot and the VM reading the snapshot must be the + * same version, must be built in the same DEBUG/RELEASE/PRODUCT mode, must + * be targeting the same architecture, and must both be in checked mode or + * both in unchecked mode. + * + * The buffers are scope allocated and are only valid until the next call to + * Dart_ExitScope. + * + * \return A valid handle if no error occurs during the operation. + */ +DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle +Dart_CreateAppJITSnapshotAsBlobs(uint8_t** isolate_snapshot_data_buffer, + intptr_t* isolate_snapshot_data_size, + uint8_t** isolate_snapshot_instructions_buffer, + intptr_t* isolate_snapshot_instructions_size); + +/** + * Like Dart_CreateAppJITSnapshotAsBlobs, but also creates a new VM snapshot. + */ +DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle +Dart_CreateCoreJITSnapshotAsBlobs( + uint8_t** vm_snapshot_data_buffer, + intptr_t* vm_snapshot_data_size, + uint8_t** vm_snapshot_instructions_buffer, + intptr_t* vm_snapshot_instructions_size, + uint8_t** isolate_snapshot_data_buffer, + intptr_t* isolate_snapshot_data_size, + uint8_t** isolate_snapshot_instructions_buffer, + intptr_t* isolate_snapshot_instructions_size); + +/** + * Get obfuscation map for precompiled code. + * + * Obfuscation map is encoded as a JSON array of pairs (original name, + * obfuscated name). + * + * \return Returns an error handler if the VM was built in a mode that does not + * support obfuscation. + */ +DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle +Dart_GetObfuscationMap(uint8_t** buffer, intptr_t* buffer_length); + +/** + * Returns whether the VM only supports running from precompiled snapshots and + * not from any other kind of snapshot or from source (that is, the VM was + * compiled with DART_PRECOMPILED_RUNTIME). + */ +DART_EXPORT bool Dart_IsPrecompiledRuntime(); + +/** + * Print a native stack trace. Used for crash handling. + * + * If context is NULL, prints the current stack trace. Otherwise, context + * should be a CONTEXT* (Windows) or ucontext_t* (POSIX) from a signal handler + * running on the current thread. + */ +DART_EXPORT void Dart_DumpNativeStackTrace(void* context); + +/** + * Indicate that the process is about to abort, and the Dart VM should not + * attempt to cleanup resources. + */ +DART_EXPORT void Dart_PrepareToAbort(); + +#endif /* INCLUDE_DART_API_H_ */ /* NOLINT */ diff --git a/flutter/realm_flutter/ios/Classes/include/dart_api_dl.h b/flutter/realm_flutter/ios/Classes/include/dart_api_dl.h new file mode 100644 index 000000000..4ad4d847b --- /dev/null +++ b/flutter/realm_flutter/ios/Classes/include/dart_api_dl.h @@ -0,0 +1,690 @@ +/* + * Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file + * for details. All rights reserved. Use of this source code is governed by a + * BSD-style license that can be found in the LICENSE file. + */ + +#ifndef RUNTIME_INCLUDE_DART_API_DL_H_ +#define RUNTIME_INCLUDE_DART_API_DL_H_ + +#include "dart_api.h" +#include "dart_native_api.h" + +/** \mainpage Dynamically Linked Dart API + * + * This exposes a subset of symbols from dart_api.h and dart_native_api.h + * available in every Dart embedder through dynamic linking. + * + * All symbols are postfixed with _DL to indicate that they are dynamically + * linked and to prevent conflicts with the original symbol. + * + * Link `dart_api_dl.c` file into your library and invoke + * `Dart_InitializeApiDL` with `NativeApi.initializeApiDLData`. + */ + +#ifdef __cplusplus +#define DART_EXTERN extern "C" +#else +#define DART_EXTERN extern +#endif + +DART_EXTERN intptr_t Dart_InitializeApiDL(void* data); + +// ============================================================================ +// IMPORTANT! Never update these signatures without properly updating +// DART_API_DL_MAJOR_VERSION and DART_API_DL_MINOR_VERSION. +// +// Verbatim copy of `dart_native_api.h` and `dart_api.h` symbol names and types +// to trigger compile-time errors if the sybols in those files are updated +// without updating these. +// +// Function return and argument types, and typedefs are carbon copied. Structs +// are typechecked nominally in C/C++, so they are not copied, instead a +// comment is added to their definition. +typedef int64_t Dart_Port_DL; + +typedef void (*Dart_NativeMessageHandler_DL)(Dart_Port_DL dest_port_id, + Dart_CObject* message); + +// dart_native_api.h symbols can be called on any thread. +#define DART_NATIVE_API_DL_SYMBOLS(F) \ + /***** dart_native_api.h *****/ \ + /* Dart_Port */ \ + F(Dart_PostCObject, bool, (Dart_Port_DL port_id, Dart_CObject * message)) \ + F(Dart_PostInteger, bool, (Dart_Port_DL port_id, int64_t message)) \ + F(Dart_NewNativePort, Dart_Port_DL, \ + (const char* name, Dart_NativeMessageHandler_DL handler, \ + bool handle_concurrently)) \ + F(Dart_CloseNativePort, bool, (Dart_Port_DL native_port_id)) + +// dart_api.h symbols can only be called on Dart threads. +#define DART_API_DL_SYMBOLS(F) \ + /***** dart_api.h *****/ \ + /* Errors */ \ + F(Dart_IsError, bool, (Dart_Handle handle)) \ + F(Dart_IsApiError, bool, (Dart_Handle handle)) \ + F(Dart_IsUnhandledExceptionError, bool, (Dart_Handle handle)) \ + F(Dart_IsCompilationError, bool, (Dart_Handle handle)) \ + F(Dart_IsFatalError, bool, (Dart_Handle handle)) \ + F(Dart_GetError, const char*, (Dart_Handle handle)) \ + F(Dart_ErrorHasException, bool, (Dart_Handle handle)) \ + F(Dart_ErrorGetException, Dart_Handle, (Dart_Handle handle)) \ + F(Dart_ErrorGetStackTrace, Dart_Handle, (Dart_Handle handle)) \ + F(Dart_NewApiError, Dart_Handle, (const char* error)) \ + F(Dart_NewCompilationError, Dart_Handle, (const char* error)) \ + F(Dart_NewUnhandledExceptionError, Dart_Handle, (Dart_Handle exception)) \ + F(Dart_PropagateError, void, (Dart_Handle handle)) \ + /* Dart_Handle, Dart_PersistentHandle, Dart_WeakPersistentHandle */ \ + F(Dart_HandleFromPersistent, Dart_Handle, (Dart_PersistentHandle object)) \ + F(Dart_HandleFromWeakPersistent, Dart_Handle, \ + (Dart_WeakPersistentHandle object)) \ + F(Dart_NewPersistentHandle, Dart_PersistentHandle, (Dart_Handle object)) \ + F(Dart_SetPersistentHandle, void, \ + (Dart_PersistentHandle obj1, Dart_Handle obj2)) \ + F(Dart_DeletePersistentHandle, void, (Dart_PersistentHandle object)) \ + F(Dart_NewWeakPersistentHandle, Dart_WeakPersistentHandle, \ + (Dart_Handle object, void* peer, intptr_t external_allocation_size, \ + Dart_WeakPersistentHandleFinalizer callback)) \ + F(Dart_DeleteWeakPersistentHandle, void, (Dart_WeakPersistentHandle object)) \ + F(Dart_UpdateExternalSize, void, \ + (Dart_WeakPersistentHandle object, intptr_t external_allocation_size)) \ + F(Dart_NewFinalizableHandle, Dart_FinalizableHandle, \ + (Dart_Handle object, void* peer, intptr_t external_allocation_size, \ + Dart_HandleFinalizer callback)) \ + F(Dart_DeleteFinalizableHandle, void, \ + (Dart_FinalizableHandle object, Dart_Handle strong_ref_to_object)) \ + F(Dart_UpdateFinalizableExternalSize, void, \ + (Dart_FinalizableHandle object, Dart_Handle strong_ref_to_object, \ + intptr_t external_allocation_size)) \ + /* Dart_Port */ \ + F(Dart_Post, bool, (Dart_Port_DL port_id, Dart_Handle object)) \ + F(Dart_NewSendPort, Dart_Handle, (Dart_Port_DL port_id)) \ + F(Dart_SendPortGetId, Dart_Handle, \ + (Dart_Handle port, Dart_Port_DL * port_id)) \ + /* Scopes */ \ + F(Dart_EnterScope, void, ()) \ + F(Dart_ExitScope, void, ()) \ + \ + \ +/* extenders*/ \ + \ +F(Dart_Allocate, Dart_Handle, (Dart_Handle type)) \ + \ +F(Dart_AllocateWithNativeFields, Dart_Handle, ( \ + Dart_Handle type, \ + intptr_t num_native_fields, \ + const intptr_t* native_fields)) \ + \ +F(Dart_BooleanValue, Dart_Handle, (Dart_Handle boolean_obj, \ + bool* value)) \ + \ +F(Dart_ClassLibrary, Dart_Handle, (Dart_Handle cls_type)) \ + \ +F(Dart_ClassName, Dart_Handle, (Dart_Handle cls_type)) \ + \ +F(Dart_Cleanup, char*, ()) \ + \ +F(Dart_ClosureFunction, Dart_Handle, (Dart_Handle closure)) \ + \ +F(Dart_CreateIsolateGroup, Dart_Isolate, ( \ + const char* script_uri, \ + const char* name, \ + const uint8_t* isolate_snapshot_data, \ + const uint8_t* isolate_snapshot_instructions, \ + Dart_IsolateFlags* flags, \ + void* isolate_group_data, \ + void* isolate_data, \ + char** error)) \ + \ +F(Dart_CreateIsolateGroupFromKernel, Dart_Isolate, ( \ + const char* script_uri, \ + const char* name, \ + const uint8_t* kernel_buffer, \ + intptr_t kernel_buffer_size, \ + Dart_IsolateFlags* flags, \ + void* isolate_group_data, \ + void* isolate_data, \ + char** error)) \ + \ +F(Dart_CurrentIsolate, Dart_Isolate, ()) \ + \ +F(Dart_CurrentIsolateData, void*, ()) \ + \ +F(Dart_CurrentIsolateGroup, Dart_IsolateGroup, ()) \ + \ +F(Dart_CurrentIsolateGroupData, void*, ()) \ + \ +F(Dart_DebugName, Dart_Handle, ()) \ + \ +F(Dart_DoubleValue, Dart_Handle, (Dart_Handle double_obj, \ + double* value)) \ + \ +F(Dart_DumpNativeStackTrace, void, (void* context)) \ + \ +F(Dart_EmptyString, Dart_Handle, ()) \ + \ +F(Dart_EnterIsolate, void, (Dart_Isolate isolate)) \ + \ +F(Dart_ExitIsolate, void, ()) \ + \ +F(Dart_False, Dart_Handle, ()) \ + \ +F(Dart_FunctionIsStatic, Dart_Handle, (Dart_Handle function, \ + bool* is_static)) \ + \ +F(Dart_FunctionName, Dart_Handle, (Dart_Handle function)) \ + \ +F(Dart_FunctionOwner, Dart_Handle, (Dart_Handle function)) \ + \ +F(Dart_GetClass, Dart_Handle, (Dart_Handle library, \ + Dart_Handle class_name)) \ + \ +F(Dart_GetDataFromByteBuffer, Dart_Handle, ( \ + Dart_Handle byte_buffer)) \ + \ +F(Dart_GetField, Dart_Handle, (Dart_Handle container, \ + Dart_Handle name)) \ + \ +F(Dart_GetImportsOfScheme, Dart_Handle, (Dart_Handle scheme)) \ + \ +F(Dart_GetLoadedLibraries, Dart_Handle, ()) \ + \ +F(Dart_GetMessageNotifyCallback, Dart_MessageNotifyCallback, ()) \ + \ +F(Dart_GetNativeArguments, Dart_Handle, ( \ + Dart_NativeArguments args, \ + int num_arguments, \ + const Dart_NativeArgument_Descriptor* arg_descriptors, \ + Dart_NativeArgument_Value* arg_values)) \ + \ +F(Dart_GetNativeArgument, Dart_Handle, ( \ + Dart_NativeArguments args, \ + int index)) \ + \ +F(Dart_GetNativeBooleanArgument, Dart_Handle, ( \ + Dart_NativeArguments args, \ + int index, \ + bool* value)) \ + \ +F(Dart_GetNativeDoubleArgument, Dart_Handle, ( \ + Dart_NativeArguments args, \ + int index, \ + double* value)) \ + \ +F(Dart_GetNativeArgumentCount, int, (Dart_NativeArguments args)) \ + \ +F(Dart_GetNativeFieldsOfArgument, Dart_Handle, ( \ + Dart_NativeArguments args, \ + int arg_index, \ + int num_fields, \ + intptr_t* field_values)) \ + \ +F(Dart_GetNativeInstanceField, Dart_Handle, (Dart_Handle obj, \ + int index, \ + intptr_t* value)) \ + \ +F(Dart_GetNativeInstanceFieldCount, Dart_Handle, ( \ + Dart_Handle obj, \ + int* count)) \ + \ +F(Dart_GetNativeIntegerArgument, Dart_Handle, ( \ + Dart_NativeArguments args, \ + int index, \ + int64_t* value)) \ + \ +F(Dart_GetNativeIsolateGroupData, void*, ( \ + Dart_NativeArguments args)) \ + \ +F(Dart_SetNativeResolver, Dart_Handle, (Dart_Handle library, \ + Dart_NativeEntryResolver resolver, \ + Dart_NativeEntrySymbol symbol)) \ + \ +F(Dart_GetNativeResolver, Dart_Handle, (Dart_Handle library, \ + Dart_NativeEntryResolver* resolver)) \ + \ +F(Dart_GetNativeStringArgument, Dart_Handle, ( \ + Dart_NativeArguments args, \ + int arg_index, \ + void** peer)) \ + \ +F(Dart_GetNativeSymbol, Dart_Handle, ( \ + Dart_Handle library, \ + Dart_NativeEntrySymbol* resolver)) \ + \ +F(Dart_GetNonNullableType, Dart_Handle, (Dart_Handle library, \ + Dart_Handle class_name, \ + intptr_t number_of_type_arguments, \ + Dart_Handle* type_arguments)) \ + \ +F(Dart_GetNullableType, Dart_Handle, ( \ + Dart_Handle library, \ + Dart_Handle class_name, \ + intptr_t number_of_type_arguments, \ + Dart_Handle* type_arguments)) \ + \ +F(Dart_GetPeer, Dart_Handle, (Dart_Handle object, void** peer)) \ + \ +F(Dart_GetStaticMethodClosure, Dart_Handle, ( \ + Dart_Handle library, \ + Dart_Handle cls_type, \ + Dart_Handle function_name)) \ + \ +F(Dart_GetStickyError, Dart_Handle, ()) \ + \ +F(Dart_GetType, Dart_Handle, (Dart_Handle library, \ + Dart_Handle class_name, \ + intptr_t number_of_type_arguments, \ + Dart_Handle* type_arguments)) \ + \ +F(Dart_GetTypeOfExternalTypedData, Dart_TypedData_Type, ( \ + Dart_Handle object)) \ + \ +F(Dart_GetTypeOfTypedData, Dart_TypedData_Type, ( \ + Dart_Handle object)) \ + \ +F(Dart_HasStickyError, bool, ()) \ + \ +F(Dart_IdentityEquals, bool, (Dart_Handle obj1, \ + Dart_Handle obj2)) \ + \ +F(Dart_InstanceGetType, Dart_Handle, (Dart_Handle instance)) \ + \ +F(Dart_IntegerFitsIntoInt64, Dart_Handle, (Dart_Handle integer, \ + bool* fits)) \ + \ +F(Dart_IntegerFitsIntoUint64, Dart_Handle, (Dart_Handle integer, \ + bool* fits)) \ + \ +F(Dart_IntegerToHexCString, Dart_Handle, (Dart_Handle integer, \ + const char** value)) \ + \ +F(Dart_IntegerToInt64, Dart_Handle, (Dart_Handle integer, \ + int64_t* value)) \ + \ +F(Dart_IntegerToUint64, Dart_Handle, (Dart_Handle integer, \ + uint64_t* value)) \ + \ +F(Dart_Invoke, Dart_Handle, (Dart_Handle target, \ + Dart_Handle name, \ + int number_of_arguments, \ + Dart_Handle* arguments)) \ + \ +F(Dart_InvokeClosure, Dart_Handle, (Dart_Handle closure, \ + int number_of_arguments, \ + Dart_Handle* arguments)) \ + \ +F(Dart_InvokeConstructor, Dart_Handle, (Dart_Handle object, \ + Dart_Handle name, \ + int number_of_arguments, \ + Dart_Handle* arguments)) \ + \ +F(Dart_IsBoolean, bool, (Dart_Handle object)) \ + \ +F(Dart_IsByteBuffer, bool, (Dart_Handle object)) \ + \ +F(Dart_IsClosure, bool, (Dart_Handle object)) \ + \ +F(Dart_IsDouble, bool, (Dart_Handle object)) \ + \ +F(Dart_IsExternalString, bool, (Dart_Handle object)) \ + \ +F(Dart_IsFunction, bool, (Dart_Handle handle)) \ + \ +F(Dart_IsFuture, bool, (Dart_Handle object)) \ + \ +F(Dart_IsInstance, bool, (Dart_Handle object)) \ + \ +F(Dart_IsInteger, bool, (Dart_Handle object)) \ + \ +F(Dart_IsKernel, bool, (const uint8_t* buffer, \ + intptr_t buffer_size)) \ + \ +F(Dart_IsKernelIsolate, bool, (Dart_Isolate isolate)) \ + \ +F(Dart_IsLegacyType, Dart_Handle, (Dart_Handle type, \ + bool* result)) \ + \ +F(Dart_IsLibrary, bool, (Dart_Handle object)) \ + \ +F(Dart_IsList, bool, (Dart_Handle object)) \ + \ +F(Dart_IsMap, bool, (Dart_Handle object)) \ + \ +F(Dart_IsNonNullableType, Dart_Handle, (Dart_Handle type, \ + bool* result)) \ + \ +F(Dart_IsNull, bool, (Dart_Handle object)) \ + \ +F(Dart_IsNumber, bool, (Dart_Handle object)) \ + \ +F(Dart_IsolateData, void*, (Dart_Isolate isolate)) \ + \ +F(Dart_IsolateFlagsInitialize, void, (Dart_IsolateFlags* flags)) \ + \ +F(Dart_IsolateGroupData, void*, (Dart_Isolate isolate)) \ + \ +F(Dart_IsolateMakeRunnable, char*, (Dart_Isolate isolate)) \ + \ +F(Dart_IsolateServiceId, const char*, (Dart_Isolate isolate)) \ + \ +F(Dart_IsPausedOnExit, bool, ()) \ + \ +F(Dart_IsPausedOnStart, bool, ()) \ + \ +F(Dart_IsPrecompiledRuntime, bool, ()) \ + \ +F(Dart_IsServiceIsolate, bool, (Dart_Isolate isolate)) \ + \ +F(Dart_IsString, bool, (Dart_Handle object)) \ + \ +F(Dart_IsStringLatin1, bool, (Dart_Handle object)) \ + \ +F(Dart_IsTearOff, bool, (Dart_Handle object)) \ + \ +F(Dart_IsType, bool, (Dart_Handle handle)) \ + \ +F(Dart_IsTypedData, bool, (Dart_Handle object)) \ + \ +F(Dart_IsTypeVariable, bool, (Dart_Handle handle)) \ + \ +F(Dart_IsVariable, bool, (Dart_Handle handle)) \ + \ +F(Dart_IsVMFlagSet, bool, (const char* flag_name)) \ + \ +F(Dart_KernelIsolateIsRunning, bool, ()) \ + \ +F(Dart_KernelListDependencies, Dart_KernelCompilationResult, ()) \ + \ +F(Dart_KernelPort, Dart_Port, ()) \ + \ +F(Dart_KillIsolate, void, (Dart_Isolate isolate)) \ + \ +F(Dart_LibraryHandleError, Dart_Handle, (Dart_Handle library, \ + Dart_Handle error)) \ + \ +F(Dart_LibraryResolvedUrl, Dart_Handle, (Dart_Handle library)) \ + \ +F(Dart_LibraryUrl, Dart_Handle, (Dart_Handle library)) \ + \ +F(Dart_ListGetAsBytes, Dart_Handle, (Dart_Handle list, \ + intptr_t offset, \ + uint8_t* native_array, \ + intptr_t length)) \ + \ +F(Dart_ListGetAt, Dart_Handle, (Dart_Handle list, \ + intptr_t index)) \ + \ +F(Dart_ListGetRange, Dart_Handle, (Dart_Handle list, \ + intptr_t offset, \ + intptr_t length, \ + Dart_Handle* result)) \ + \ +F(Dart_ListLength, Dart_Handle, (Dart_Handle list, \ + intptr_t* length)) \ + \ +F(Dart_ListSetAsBytes, Dart_Handle, (Dart_Handle list, \ + intptr_t offset, \ + const uint8_t* native_array, \ + intptr_t length)) \ + \ +F(Dart_ListSetAt, Dart_Handle, (Dart_Handle list, \ + intptr_t index, \ + Dart_Handle value)) \ + \ +F(Dart_LoadLibraryFromKernel, Dart_Handle, ( \ + const uint8_t* kernel_buffer, \ + intptr_t kernel_buffer_size)) \ + \ +F(Dart_LoadScriptFromKernel, Dart_Handle, ( \ + const uint8_t* kernel_buffer, \ + intptr_t kernel_size)) \ + \ +F(Dart_LookupLibrary, Dart_Handle, (Dart_Handle url)) \ + \ +F(Dart_MapContainsKey, Dart_Handle, (Dart_Handle map, \ + Dart_Handle key)) \ + \ +F(Dart_MapGetAt, Dart_Handle, (Dart_Handle map, Dart_Handle key)) \ + \ +F(Dart_MapKeys, Dart_Handle, (Dart_Handle map)) \ + \ +F(Dart_New, Dart_Handle, (Dart_Handle type, \ + Dart_Handle constructor_name, \ + int number_of_arguments, \ + Dart_Handle* arguments)) \ + \ +F(Dart_NewBoolean, Dart_Handle, (bool value)) \ + \ +F(Dart_NewByteBuffer, Dart_Handle, (Dart_Handle typed_data)) \ + \ +F(Dart_NewDouble, Dart_Handle, (double value)) \ + \ +F(Dart_NewExternalLatin1String, Dart_Handle, ( \ + const uint8_t* latin1_array, \ + intptr_t length, \ + void* peer, \ + intptr_t external_allocation_size, \ + Dart_WeakPersistentHandleFinalizer callback)) \ + \ +F(Dart_NewExternalTypedData, Dart_Handle, ( \ + Dart_TypedData_Type type, \ + void* data, \ + intptr_t length)) \ + \ +F(Dart_NewExternalTypedDataWithFinalizer, Dart_Handle, ( \ + Dart_TypedData_Type type, \ + void* data, \ + intptr_t length, \ + void* peer, \ + intptr_t external_allocation_size, \ + Dart_WeakPersistentHandleFinalizer callback)) \ + \ +F(Dart_NewExternalUTF16String, Dart_Handle, ( \ + const uint16_t* utf16_array, \ + intptr_t length, \ + void* peer, \ + intptr_t external_allocation_size, \ + Dart_WeakPersistentHandleFinalizer callback)) \ + \ +F(Dart_NewInteger, Dart_Handle, (int64_t value)) \ + \ +F(Dart_NewIntegerFromHexCString, Dart_Handle, ( \ + const char* value)) \ + \ +F(Dart_NewIntegerFromUint64, Dart_Handle, (uint64_t value)) \ + \ +F(Dart_NewList, Dart_Handle, (intptr_t length)) \ + \ +F(Dart_NewListOf, Dart_Handle, (Dart_CoreType_Id element_type_id, \ + intptr_t length)) \ + \ +F(Dart_NewListOfType, Dart_Handle, (Dart_Handle element_type, \ + intptr_t length)) \ + \ +F(Dart_NewListOfTypeFilled, Dart_Handle, ( \ + Dart_Handle element_type, \ + Dart_Handle fill_object, \ + intptr_t length)) \ + \ +F(Dart_NewStringFromCString, Dart_Handle, (const char* str)) \ + \ +F(Dart_NewStringFromUTF16, Dart_Handle, ( \ + const uint16_t* utf16_array, \ + intptr_t length)) \ + \ +F(Dart_NewStringFromUTF32, Dart_Handle, ( \ + const int32_t* utf32_array, \ + intptr_t length)) \ + \ +F(Dart_NewStringFromUTF8, Dart_Handle, ( \ + const uint8_t* utf8_array, \ + intptr_t length)) \ + \ +F(Dart_NewTypedData, Dart_Handle, (Dart_TypedData_Type type, \ + intptr_t length)) \ + \ +F(Dart_NotifyIdle, void, (int64_t deadline)) \ + \ +F(Dart_NotifyLowMemory, void, ()) \ + \ +F(Dart_Null, Dart_Handle, ()) \ + \ +F(Dart_ObjectEquals, Dart_Handle, (Dart_Handle obj1, \ + Dart_Handle obj2, \ + bool* equal)) \ + \ +F(Dart_ObjectIsType, Dart_Handle, (Dart_Handle object, \ + Dart_Handle type, \ + bool* instanceof)) \ + \ +F(Dart_PrepareToAbort, void, ()) \ + \ +F(Dart_ReThrowException, Dart_Handle, (Dart_Handle exception, \ + Dart_Handle stacktrace)) \ + \ +F(Dart_RootLibrary, Dart_Handle, ()) \ + \ +F(Dart_ScopeAllocate, uint8_t*, (intptr_t size)) \ + \ +F(Dart_SetBooleanReturnValue, void, (Dart_NativeArguments args, \ + bool retval)) \ + \ +F(Dart_SetDartLibrarySourcesKernel, void, ( \ + const uint8_t* platform_kernel, \ + const intptr_t platform_kernel_size)) \ + \ +F(Dart_SetDoubleReturnValue, void, (Dart_NativeArguments args, \ + double retval)) \ + \ +F(Dart_SetEnvironmentCallback, Dart_Handle, ( \ + Dart_EnvironmentCallback callback)) \ + \ +F(Dart_SetField, Dart_Handle, (Dart_Handle container, \ + Dart_Handle name, \ + Dart_Handle value)) \ + \ +F(Dart_SetIntegerReturnValue, void, (Dart_NativeArguments args, \ + int64_t retval)) \ + \ +F(Dart_SetLibraryTagHandler, Dart_Handle, ( \ + Dart_LibraryTagHandler handler)) \ + \ +F(Dart_SetMessageNotifyCallback, void, ( \ + Dart_MessageNotifyCallback message_notify_callback)) \ + \ +F(Dart_SetNativeInstanceField, Dart_Handle, (Dart_Handle obj, \ + int index, \ + intptr_t value)) \ + \ +F(Dart_SetPausedOnExit, void, (bool paused)) \ + \ +F(Dart_SetPausedOnStart, void, (bool paused)) \ + \ +F(Dart_SetPeer, Dart_Handle, (Dart_Handle object, void* peer)) \ + \ +F(Dart_SetReturnValue, void, (Dart_NativeArguments args, \ + Dart_Handle retval)) \ + \ +F(Dart_SetRootLibrary, Dart_Handle, (Dart_Handle library)) \ + \ +F(Dart_SetShouldPauseOnExit, void, (bool should_pause)) \ + \ +F(Dart_SetShouldPauseOnStart, void, (bool should_pause)) \ + \ +F(Dart_SetStickyError, void, (Dart_Handle error)) \ + \ +F(Dart_SetWeakHandleReturnValue, void, ( \ + Dart_NativeArguments args, \ + Dart_WeakPersistentHandle rval)) \ + \ +F(Dart_ShouldPauseOnExit, bool, ()) \ + \ +F(Dart_ShouldPauseOnStart, bool, ()) \ + \ +F(Dart_WaitForEvent, Dart_Handle, (int64_t timeout_millis)) \ + \ +F(Dart_WriteProfileToTimeline, bool, (Dart_Port main_port, \ + char** error)) \ + \ +F(Dart_ShutdownIsolate, void, ()) \ + \ +F(Dart_StartProfiling, void, ()) \ + \ +F(Dart_StopProfiling, void, ()) \ + \ +F(Dart_StringGetProperties, Dart_Handle, (Dart_Handle str, \ + intptr_t* char_size, \ + intptr_t* str_len, \ + void** peer)) \ + \ +F(Dart_StringLength, Dart_Handle, (Dart_Handle str, \ + intptr_t* length)) \ + \ +F(Dart_StringStorageSize, Dart_Handle, (Dart_Handle str, \ + intptr_t* size)) \ + \ +F(Dart_StringToCString, Dart_Handle, (Dart_Handle str, \ + const char** cstr)) \ + \ +F(Dart_StringToLatin1, Dart_Handle, (Dart_Handle str, \ + uint8_t* latin1_array, \ + intptr_t* length)) \ + \ +F(Dart_StringToUTF16, Dart_Handle, (Dart_Handle str, \ + uint16_t* utf16_array, \ + intptr_t* length)) \ + \ +F(Dart_StringToUTF8, Dart_Handle, (Dart_Handle str, \ + uint8_t** utf8_array, \ + intptr_t* length)) \ + \ +F(Dart_ThreadDisableProfiling, void, ()) \ + \ +F(Dart_ThreadEnableProfiling, void, ()) \ + \ +F(Dart_ThrowException, Dart_Handle, (Dart_Handle exception)) \ + \ +F(Dart_ToString, Dart_Handle, (Dart_Handle object)) \ + \ +F(Dart_True, Dart_Handle, ()) \ + \ +F(Dart_TypedDataAcquireData, Dart_Handle, (Dart_Handle object, \ + Dart_TypedData_Type* type, \ + void** data, \ + intptr_t* len)) \ + \ +F(Dart_TypedDataReleaseData, Dart_Handle, (Dart_Handle object)) \ + \ +F(Dart_TypeDynamic, Dart_Handle, ()) \ + \ +F(Dart_TypeNever, Dart_Handle, ()) \ + \ +F(Dart_TypeToNonNullableType, Dart_Handle, (Dart_Handle type)) \ + \ +F(Dart_TypeToNullableType, Dart_Handle, (Dart_Handle type)) \ + \ +F(Dart_TypeVoid, Dart_Handle, ()) \ + \ +F(Dart_VersionString, const char*, ()) \ + + +#define DART_API_ALL_DL_SYMBOLS(F) \ + DART_NATIVE_API_DL_SYMBOLS(F) \ + DART_API_DL_SYMBOLS(F) +// IMPORTANT! Never update these signatures without properly updating +// DART_API_DL_MAJOR_VERSION and DART_API_DL_MINOR_VERSION. +// +// End of verbatim copy. +// ============================================================================ + +#define DART_API_DL_DECLARATIONS(name, R, A) \ + typedef R(*name##_Type) A; \ + DART_EXTERN name##_Type name##_DL; + +DART_API_ALL_DL_SYMBOLS(DART_API_DL_DECLARATIONS) + +#undef DART_API_DL_DEFINITIONS + +#undef DART_EXTERN + +#endif /* RUNTIME_INCLUDE_DART_API_DL_H_ */ /* NOLINT */ diff --git a/flutter/realm_flutter/ios/Classes/include/dart_native_api.h b/flutter/realm_flutter/ios/Classes/include/dart_native_api.h new file mode 100644 index 000000000..495d45485 --- /dev/null +++ b/flutter/realm_flutter/ios/Classes/include/dart_native_api.h @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file + * for details. All rights reserved. Use of this source code is governed by a + * BSD-style license that can be found in the LICENSE file. + */ + +#ifndef RUNTIME_INCLUDE_DART_NATIVE_API_H_ +#define RUNTIME_INCLUDE_DART_NATIVE_API_H_ + +#include "dart_api.h" /* NOLINT */ + +/* + * ========================================== + * Message sending/receiving from native code + * ========================================== + */ + +/** + * A Dart_CObject is used for representing Dart objects as native C + * data outside the Dart heap. These objects are totally detached from + * the Dart heap. Only a subset of the Dart objects have a + * representation as a Dart_CObject. + * + * The string encoding in the 'value.as_string' is UTF-8. + * + * All the different types from dart:typed_data are exposed as type + * kTypedData. The specific type from dart:typed_data is in the type + * field of the as_typed_data structure. The length in the + * as_typed_data structure is always in bytes. + * + * The data for kTypedData is copied on message send and ownership remains with + * the caller. The ownership of data for kExternalTyped is passed to the VM on + * message send and returned when the VM invokes the + * Dart_WeakPersistentHandleFinalizer callback; a non-NULL callback must be + * provided. + */ +typedef enum { + Dart_CObject_kNull = 0, + Dart_CObject_kBool, + Dart_CObject_kInt32, + Dart_CObject_kInt64, + Dart_CObject_kDouble, + Dart_CObject_kString, + Dart_CObject_kArray, + Dart_CObject_kTypedData, + Dart_CObject_kExternalTypedData, + Dart_CObject_kSendPort, + Dart_CObject_kCapability, + Dart_CObject_kUnsupported, + Dart_CObject_kNumberOfTypes +} Dart_CObject_Type; + +typedef struct _Dart_CObject { + Dart_CObject_Type type; + union { + bool as_bool; + int32_t as_int32; + int64_t as_int64; + double as_double; + char* as_string; + struct { + Dart_Port id; + Dart_Port origin_id; + } as_send_port; + struct { + int64_t id; + } as_capability; + struct { + intptr_t length; + struct _Dart_CObject** values; + } as_array; + struct { + Dart_TypedData_Type type; + intptr_t length; + uint8_t* values; + } as_typed_data; + struct { + Dart_TypedData_Type type; + intptr_t length; + uint8_t* data; + void* peer; + Dart_WeakPersistentHandleFinalizer callback; + } as_external_typed_data; + } value; +} Dart_CObject; +// This struct is versioned by DART_API_DL_MAJOR_VERSION, bump the version when +// changing this struct. + +/** + * Posts a message on some port. The message will contain the Dart_CObject + * object graph rooted in 'message'. + * + * While the message is being sent the state of the graph of Dart_CObject + * structures rooted in 'message' should not be accessed, as the message + * generation will make temporary modifications to the data. When the message + * has been sent the graph will be fully restored. + * + * If true is returned, the message was enqueued, and finalizers for external + * typed data will eventually run, even if the receiving isolate shuts down + * before processing the message. If false is returned, the message was not + * enqueued and ownership of external typed data in the message remains with the + * caller. + * + * This function may be called on any thread when the VM is running (that is, + * after Dart_Initialize has returned and before Dart_Cleanup has been called). + * + * \param port_id The destination port. + * \param message The message to send. + * + * \return True if the message was posted. + */ +DART_EXPORT bool Dart_PostCObject(Dart_Port port_id, Dart_CObject* message); + +/** + * Posts a message on some port. The message will contain the integer 'message'. + * + * \param port_id The destination port. + * \param message The message to send. + * + * \return True if the message was posted. + */ +DART_EXPORT bool Dart_PostInteger(Dart_Port port_id, int64_t message); + +/** + * A native message handler. + * + * This handler is associated with a native port by calling + * Dart_NewNativePort. + * + * The message received is decoded into the message structure. The + * lifetime of the message data is controlled by the caller. All the + * data references from the message are allocated by the caller and + * will be reclaimed when returning to it. + */ +typedef void (*Dart_NativeMessageHandler)(Dart_Port dest_port_id, + Dart_CObject* message); + +/** + * Creates a new native port. When messages are received on this + * native port, then they will be dispatched to the provided native + * message handler. + * + * \param name The name of this port in debugging messages. + * \param handler The C handler to run when messages arrive on the port. + * \param handle_concurrently Is it okay to process requests on this + * native port concurrently? + * + * \return If successful, returns the port id for the native port. In + * case of error, returns ILLEGAL_PORT. + */ +DART_EXPORT Dart_Port Dart_NewNativePort(const char* name, + Dart_NativeMessageHandler handler, + bool handle_concurrently); +/* TODO(turnidge): Currently handle_concurrently is ignored. */ + +/** + * Closes the native port with the given id. + * + * The port must have been allocated by a call to Dart_NewNativePort. + * + * \param native_port_id The id of the native port to close. + * + * \return Returns true if the port was closed successfully. + */ +DART_EXPORT bool Dart_CloseNativePort(Dart_Port native_port_id); + +/* + * ================== + * Verification Tools + * ================== + */ + +/** + * Forces all loaded classes and functions to be compiled eagerly in + * the current isolate.. + * + * TODO(turnidge): Document. + */ +DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle Dart_CompileAll(); + +DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle Dart_ReadAllBytecode(); + +/** + * Finalizes all classes. + */ +DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle Dart_FinalizeAllClasses(); + +/* This function is intentionally undocumented. + * + * It should not be used outside internal tests. + */ +DART_EXPORT void* Dart_ExecuteInternalCommand(const char* command, void* arg); + +#endif /* INCLUDE_DART_NATIVE_API_H_ */ /* NOLINT */ diff --git a/flutter/realm_flutter/ios/Classes/include/dart_version.h b/flutter/realm_flutter/ios/Classes/include/dart_version.h new file mode 100644 index 000000000..d2d904dd9 --- /dev/null +++ b/flutter/realm_flutter/ios/Classes/include/dart_version.h @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file + * for details. All rights reserved. Use of this source code is governed by a + * BSD-style license that can be found in the LICENSE file. + */ + +#ifndef RUNTIME_INCLUDE_DART_VERSION_H_ +#define RUNTIME_INCLUDE_DART_VERSION_H_ + +// On breaking changes the major version is increased. +// On backwards compatible changes the minor version is increased. +// The versioning covers the symbols exposed in dart_api_dl.h +#define DART_API_DL_MAJOR_VERSION 1 +#define DART_API_DL_MINOR_VERSION 1 + +#endif /* RUNTIME_INCLUDE_DART_VERSION_H_ */ /* NOLINT */ diff --git a/flutter/realm_flutter/ios/Classes/include/internal/dart_api_dl_impl.h b/flutter/realm_flutter/ios/Classes/include/internal/dart_api_dl_impl.h new file mode 100644 index 000000000..ad13a4b81 --- /dev/null +++ b/flutter/realm_flutter/ios/Classes/include/internal/dart_api_dl_impl.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file + * for details. All rights reserved. Use of this source code is governed by a + * BSD-style license that can be found in the LICENSE file. + */ + +#ifndef RUNTIME_INCLUDE_INTERNAL_DART_API_DL_IMPL_H_ +#define RUNTIME_INCLUDE_INTERNAL_DART_API_DL_IMPL_H_ + +typedef struct { + const char* name; + void (*function)(); +} DartApiEntry; + +typedef struct { + const int major; + const int minor; + const DartApiEntry* const functions; +} DartApi; + +#endif /* RUNTIME_INCLUDE_INTERNAL_DART_API_DL_IMPL_H_ */ /* NOLINT */ diff --git a/flutter/realm_flutter/ios/Classes/platform.mm b/flutter/realm_flutter/ios/Classes/platform.mm new file mode 100644 index 000000000..d1012698f --- /dev/null +++ b/flutter/realm_flutter/ios/Classes/platform.mm @@ -0,0 +1,163 @@ +//////////////////////////////////////////////////////////////////////////// +// +// 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 "platform.hpp" + +#include + +#include +#include +#include + +#import + +static NSString *error_description(NSError *error) { + if (NSError *underlyingError = error.userInfo[NSUnderlyingErrorKey]) { + return underlyingError.localizedDescription; + } + return error.localizedDescription; +} + +namespace realm { + +std::string default_realm_file_directory() +{ + std::string ret; + @autoreleasepool { +#if TARGET_OS_IPHONE + // On iOS the Documents directory isn't user-visible, so put files there + NSString *path = 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"]) { + NSString *identifier = [[NSBundle mainBundle] bundleIdentifier]; + if ([identifier length] == 0) { + identifier = [[[NSBundle mainBundle] executablePath] lastPathComponent]; + } + path = [path stringByAppendingPathComponent:identifier]; + + // create directory + [[NSFileManager defaultManager] createDirectoryAtPath:path + withIntermediateDirectories:YES + attributes:nil + error:nil]; + } +#endif + ret = path.UTF8String; + } + return ret; +} + +void ensure_directory_exists_for_file(const std::string &fileName) +{ + @autoreleasepool { + NSString *docsDir = [@(fileName.c_str()) stringByDeletingLastPathComponent]; + NSFileManager *manager = [NSFileManager defaultManager]; + + if ([manager fileExistsAtPath:docsDir]) { + return; + } + + NSError *error = nil; + if (![manager createDirectoryAtPath:docsDir withIntermediateDirectories:YES attributes:nil error:&error]) { + throw std::runtime_error(util::format("Failed to create directory \"%1\": %2", docsDir.UTF8String, error_description(error).UTF8String)); + } + } +} + +void copy_bundled_realm_files() +{ + @autoreleasepool { + NSString *docsDir = @(default_realm_file_directory().c_str()); + NSFileManager *manager = [NSFileManager defaultManager]; + for (id bundle in [NSBundle allBundles]) { + NSString *resourcePath = [bundle resourcePath]; + for (NSString *path in [manager enumeratorAtPath:resourcePath]) { + if (![path containsString:@".realm"]) { + continue; + } + + NSString *docsPath = [docsDir stringByAppendingPathComponent:path]; + if ([manager fileExistsAtPath:docsPath]) { + continue; + } + + NSError *error = nil; + if (![manager copyItemAtPath:[resourcePath stringByAppendingPathComponent:path] toPath:docsPath error:&error]) { + throw std::runtime_error(util::format("Failed to copy file from \"%1\" to \"%2\": %3", + path.UTF8String, docsPath.UTF8String, error_description(error).UTF8String)); + } + } + } + } +} + +void remove_realm_files_from_directory(const std::string &directory) +{ + @autoreleasepool { + NSFileManager *manager = [NSFileManager defaultManager]; + NSString *fileDir = @(directory.c_str()); + + for (NSString *path in [manager enumeratorAtPath:fileDir]) { + if (![path.pathExtension isEqualToString:@"realm"] && ![path.pathExtension isEqualToString:@"realm.lock"] && ![path.pathExtension isEqualToString:@"realm.management"]) { + continue; + } + NSError *error = nil; + NSString *filePath = [fileDir stringByAppendingPathComponent:path]; + if (![manager removeItemAtPath:filePath error:&error]) { + throw std::runtime_error(util::format("Failed to delete file at path \"%1\": %2", filePath.UTF8String, error_description(error).UTF8String)); + } + } + } +} + +void remove_file(const std::string &path) +{ + @autoreleasepool { + NSFileManager *manager = [NSFileManager defaultManager]; + NSString *filePath = @(path.c_str()); + + NSError *error = nil; + if (![manager removeItemAtPath:filePath error:&error]) { + if (error.code != NSFileNoSuchFileError) + throw std::runtime_error(util::format("Failed to delete file at path \"%1\": %2", + filePath.UTF8String, error_description(error).UTF8String)); + } + } +} + +void remove_directory(const std::string &path) +{ + remove_file(path); // works for directories too +} + + +void print(const char* fmt, ...) +{ + va_list vl; + va_start(vl, fmt); + std::string format(fmt); + format.append("\n"); + vprintf(format.c_str(), vl); + va_end(vl); +} + +} diff --git a/flutter/realm_flutter/ios/Classes/realm_flutter.cpp b/flutter/realm_flutter/ios/Classes/realm_flutter.cpp new file mode 100644 index 000000000..8e4c5a576 --- /dev/null +++ b/flutter/realm_flutter/ios/Classes/realm_flutter.cpp @@ -0,0 +1,24 @@ +// +// realm_flutter.c +// Pods-Runner +// +// Created by lubo on 2.11.20. +// +#include + +#include "dart_init.hpp" + +#include "realm_flutter.h" +void init_realm() { + Dart_Handle realmLibStr = Dart_NewStringFromCString("package:realm_flutter/realm.dart"); + Dart_Handle realmLib = Dart_LookupLibrary(realmLibStr); + if (Dart_IsError(realmLib)) { + fprintf(stderr, "Dart_LookupLibrary extension returned error"); + } else { + fprintf(stderr, "Dart_LookupLibrary extension returned success. realm.dart library found"); + + fprintf(stderr, "calling realm::dartvm::dart_init"); + realm::dartvm::dart_init(Dart_CurrentIsolate(), realmLib, ""); + fprintf(stderr, "realm::dartvm::dart_init completed"); + } +} diff --git a/flutter/realm_flutter/ios/Classes/realm_flutter.h b/flutter/realm_flutter/ios/Classes/realm_flutter.h new file mode 100644 index 000000000..4e1a2e91f --- /dev/null +++ b/flutter/realm_flutter/ios/Classes/realm_flutter.h @@ -0,0 +1,469 @@ +// +// realm_flutter.h +// Pods +// +// Created by macaka on 2.11.20. +// + +#ifndef realm_flutter_h +#define realm_flutter_h + +#include "include/dart_api_dl.h" + +#define Dart_PostCObject Dart_PostCObject_DL + +#define Dart_PostInteger Dart_PostInteger_DL + +#define Dart_NewNativePort Dart_NewNativePort_DL + +#define Dart_CloseNativePort Dart_CloseNativePort_DL + +#define Dart_IsError Dart_IsError_DL + +#define Dart_IsApiError Dart_IsApiError_DL + +#define Dart_IsUnhandledExceptionError Dart_IsUnhandledExceptionError_DL + +#define Dart_IsCompilationError Dart_IsCompilationError_DL + +#define Dart_IsFatalError Dart_IsFatalError_DL + +#define Dart_GetError Dart_GetError_DL + +#define Dart_ErrorHasException Dart_ErrorHasException_DL + +#define Dart_ErrorGetException Dart_ErrorGetException_DL + +#define Dart_ErrorGetStackTrace Dart_ErrorGetStackTrace_DL + +#define Dart_NewApiError Dart_NewApiError_DL + +#define Dart_NewCompilationError Dart_NewCompilationError_DL + +#define Dart_NewUnhandledExceptionError Dart_NewUnhandledExceptionError_DL + +#define Dart_PropagateError Dart_PropagateError_DL + +#define Dart_ToString Dart_ToString_DL + +#define Dart_IdentityEquals Dart_IdentityEquals_DL + +#define Dart_HandleFromPersistent Dart_HandleFromPersistent_DL + +#define Dart_HandleFromWeakPersistent Dart_HandleFromWeakPersistent_DL + +#define Dart_NewPersistentHandle Dart_NewPersistentHandle_DL + +#define Dart_SetPersistentHandle Dart_SetPersistentHandle_DL + +#define Dart_DeletePersistentHandle Dart_DeletePersistentHandle_DL + +#define Dart_NewWeakPersistentHandle Dart_NewWeakPersistentHandle_DL + +#define Dart_DeleteWeakPersistentHandle Dart_DeleteWeakPersistentHandle_DL + +#define Dart_Post Dart_Post_DL + +#define Dart_NewSendPort Dart_NewSendPort_DL + +#define Dart_SendPortGetId Dart_SendPortGetId_DL + +#define Dart_EnterScope Dart_EnterScope_DL + +#define Dart_ExitScope Dart_ExitScope_DL + + + + + + + + + + + + + +#define Dart_Allocate Dart_Allocate_DL + +#define Dart_AllocateWithNativeFields Dart_AllocateWithNativeFields_DL + +#define Dart_BooleanValue Dart_BooleanValue_DL + +#define Dart_ClassLibrary Dart_ClassLibrary_DL + +#define Dart_ClassName Dart_ClassName_DL + +#define Dart_Cleanup Dart_Cleanup_DL + +#define Dart_ClosureFunction Dart_ClosureFunction_DL + +#define Dart_CreateIsolateGroup Dart_CreateIsolateGroup_DL + +#define Dart_CreateIsolateGroupFromKernel Dart_CreateIsolateGroupFromKernel_DL + +#define Dart_CurrentIsolate Dart_CurrentIsolate_DL + +#define Dart_CurrentIsolateData Dart_CurrentIsolateData_DL + +#define Dart_CurrentIsolateGroup Dart_CurrentIsolateGroup_DL + +#define Dart_CurrentIsolateGroupData Dart_CurrentIsolateGroupData_DL + +#define Dart_DebugName Dart_DebugName_DL + +#define Dart_DoubleValue Dart_DoubleValue_DL + +#define Dart_DumpNativeStackTrace Dart_DumpNativeStackTrace_DL + +#define Dart_EmptyString Dart_EmptyString_DL + +#define Dart_EnterIsolate Dart_EnterIsolate_DL + +#define Dart_ExitIsolate Dart_ExitIsolate_DL + +#define Dart_False Dart_False_DL + +#define Dart_FunctionIsStatic Dart_FunctionIsStatic_DL + +#define Dart_FunctionName Dart_FunctionName_DL + +#define Dart_FunctionOwner Dart_FunctionOwner_DL + +#define Dart_GetClass Dart_GetClass_DL + +#define Dart_GetDataFromByteBuffer Dart_GetDataFromByteBuffer_DL + +#define Dart_GetField Dart_GetField_DL + +#define Dart_GetImportsOfScheme Dart_GetImportsOfScheme_DL + +#define Dart_GetLoadedLibraries Dart_GetLoadedLibraries_DL + +#define Dart_GetMessageNotifyCallback Dart_GetMessageNotifyCallback_DL + +#define Dart_GetNativeArguments Dart_GetNativeArguments_DL + +#define Dart_GetNativeArgument Dart_GetNativeArgument_DL + +#define Dart_GetNativeBooleanArgument Dart_GetNativeBooleanArgument_DL + +#define Dart_GetNativeDoubleArgument Dart_GetNativeDoubleArgument_DL + +#define Dart_GetNativeArgumentCount Dart_GetNativeArgumentCount_DL + +#define Dart_GetNativeFieldsOfArgument Dart_GetNativeFieldsOfArgument_DL + +#define Dart_GetNativeInstanceField Dart_GetNativeInstanceField_DL + +#define Dart_GetNativeInstanceFieldCount Dart_GetNativeInstanceFieldCount_DL + +#define Dart_GetNativeIntegerArgument Dart_GetNativeIntegerArgument_DL + +#define Dart_GetNativeIsolateGroupData Dart_GetNativeIsolateGroupData_DL + +#define Dart_GetNativeResolver Dart_GetNativeResolver_DL + +#define Dart_SetNativeResolver Dart_SetNativeResolver_DL + +#define Dart_GetNativeStringArgument Dart_GetNativeStringArgument_DL + +#define Dart_GetNativeSymbol Dart_GetNativeSymbol_DL + +#define Dart_GetNonNullableType Dart_GetNonNullableType_DL + +#define Dart_GetNullableType Dart_GetNullableType_DL + +#define Dart_GetPeer Dart_GetPeer_DL + +#define Dart_GetStaticMethodClosure Dart_GetStaticMethodClosure_DL + +#define Dart_GetStickyError Dart_GetStickyError_DL + +#define Dart_GetType Dart_GetType_DL + +#define Dart_GetTypeOfExternalTypedData Dart_GetTypeOfExternalTypedData_DL + +#define Dart_GetTypeOfTypedData Dart_GetTypeOfTypedData_DL + +#define Dart_HasStickyError Dart_HasStickyError_DL + +#define Dart_IdentityEquals Dart_IdentityEquals_DL + +#define Dart_InstanceGetType Dart_InstanceGetType_DL + +#define Dart_IntegerFitsIntoInt64 Dart_IntegerFitsIntoInt64_DL + +#define Dart_IntegerFitsIntoUint64 Dart_IntegerFitsIntoUint64_DL + +#define Dart_IntegerToHexCString Dart_IntegerToHexCString_DL + +#define Dart_IntegerToInt64 Dart_IntegerToInt64_DL + +#define Dart_IntegerToUint64 Dart_IntegerToUint64_DL + +#define Dart_Invoke Dart_Invoke_DL + + +#define Dart_InvokeClosure Dart_InvokeClosure_DL + +#define Dart_InvokeConstructor Dart_InvokeConstructor_DL + +#define Dart_IsBoolean Dart_IsBoolean_DL + +#define Dart_IsByteBuffer Dart_IsByteBuffer_DL + +#define Dart_IsClosure Dart_IsClosure_DL + +#define Dart_IsDouble Dart_IsDouble_DL + +#define Dart_IsExternalString Dart_IsExternalString_DL + +#define Dart_IsFunction Dart_IsFunction_DL + +#define Dart_IsFuture Dart_IsFuture_DL + +#define Dart_IsInstance Dart_IsInstance_DL + +#define Dart_IsInteger Dart_IsInteger_DL + +#define Dart_IsKernel Dart_IsKernel_DL + +#define Dart_IsKernelIsolate Dart_IsKernelIsolate_DL + +#define Dart_IsLegacyType Dart_IsLegacyType_DL + +#define Dart_IsLibrary Dart_IsLibrary_DL + +#define Dart_IsList Dart_IsList_DL + +#define Dart_IsMap Dart_IsMap_DL + +#define Dart_IsNonNullableType Dart_IsNonNullableType_DL + +#define Dart_IsNull Dart_IsNull_DL + +#define Dart_IsNumber Dart_IsNumber_DL + +#define Dart_IsolateData Dart_IsolateData_DL + +#define Dart_IsolateFlagsInitialize Dart_IsolateFlagsInitialize_DL + +#define Dart_IsolateGroupData Dart_IsolateGroupData_DL + +#define Dart_IsolateMakeRunnable Dart_IsolateMakeRunnable_DL + +#define Dart_IsolateServiceId Dart_IsolateServiceId_DL + +#define Dart_IsPausedOnExit Dart_IsPausedOnExit_DL + +#define Dart_IsPausedOnStart Dart_IsPausedOnStart_DL + +#define Dart_IsPrecompiledRuntime Dart_IsPrecompiledRuntime_DL + +#define Dart_IsServiceIsolate Dart_IsServiceIsolate_DL + +#define Dart_IsString Dart_IsString_DL + +#define Dart_IsStringLatin1 Dart_IsStringLatin1_DL + +#define Dart_IsTearOff Dart_IsTearOff_DL + +#define Dart_IsType Dart_IsType_DL + +#define Dart_IsTypedData Dart_IsTypedData_DL + +#define Dart_IsTypeVariable Dart_IsTypeVariable_DL + +#define Dart_IsVariable Dart_IsVariable_DL + +#define Dart_IsVMFlagSet Dart_IsVMFlagSet_DL + +#define Dart_KernelIsolateIsRunning Dart_KernelIsolateIsRunning_DL + +#define Dart_KernelListDependencies Dart_KernelListDependencies_DL + +#define Dart_KernelPort Dart_KernelPort_DL + +#define Dart_KillIsolate Dart_KillIsolate_DL + +#define Dart_LibraryHandleError Dart_LibraryHandleError_DL + +#define Dart_LibraryResolvedUrl Dart_LibraryResolvedUrl_DL + +#define Dart_LibraryUrl Dart_LibraryUrl_DL + +#define Dart_ListGetAsBytes Dart_ListGetAsBytes_DL + +#define Dart_ListGetAt Dart_ListGetAt_DL + +#define Dart_ListGetRange Dart_ListGetRange_DL + +#define Dart_ListLength Dart_ListLength_DL + +#define Dart_ListSetAsBytes Dart_ListSetAsBytes_DL + +#define Dart_ListSetAt Dart_ListSetAt_DL + +#define Dart_LoadLibraryFromKernel Dart_LoadLibraryFromKernel_DL + +#define Dart_LoadScriptFromKernel Dart_LoadScriptFromKernel_DL + +#define Dart_LookupLibrary Dart_LookupLibrary_DL + +#define Dart_MapContainsKey Dart_MapContainsKey_DL + +#define Dart_MapGetAt Dart_MapGetAt_DL + +#define Dart_MapKeys Dart_MapKeys_DL + +#define Dart_New Dart_New_DL + +#define Dart_NewBoolean Dart_NewBoolean_DL + +#define Dart_NewByteBuffer Dart_NewByteBuffer_DL + +#define Dart_NewDouble Dart_NewDouble_DL + +#define Dart_NewExternalLatin1String Dart_NewExternalLatin1String_DL + +#define Dart_NewExternalTypedData Dart_NewExternalTypedData_DL + +#define Dart_NewExternalTypedDataWithFinalizer Dart_NewExternalTypedDataWithFinalizer_DL + +#define Dart_NewExternalUTF16String Dart_NewExternalUTF16String_DL + +#define Dart_NewInteger Dart_NewInteger_DL + +#define Dart_NewIntegerFromHexCString Dart_NewIntegerFromHexCString_DL + +#define Dart_NewIntegerFromUint64 Dart_NewIntegerFromUint64_DL + +#define Dart_NewList Dart_NewList_DL + +#define Dart_NewListOf Dart_NewListOf_DL + +#define Dart_NewListOfType Dart_NewListOfType_DL + +#define Dart_NewListOfTypeFilled Dart_NewListOfTypeFilled_DL + +#define Dart_NewStringFromCString Dart_NewStringFromCString_DL + +#define Dart_NewStringFromUTF16 Dart_NewStringFromUTF16_DL + +#define Dart_NewStringFromUTF32 Dart_NewStringFromUTF32_DL + +#define Dart_NewStringFromUTF8 Dart_NewStringFromUTF8_DL + +#define Dart_NewTypedData Dart_NewTypedData_DL + +#define Dart_NotifyIdle Dart_NotifyIdle_DL + +#define Dart_NotifyLowMemory Dart_NotifyLowMemory_DL + +#define Dart_Null Dart_Null_DL + +#define Dart_ObjectEquals Dart_ObjectEquals_DL + +#define Dart_ObjectIsType Dart_ObjectIsType_DL + +#define Dart_PrepareToAbort Dart_PrepareToAbort_DL + +#define Dart_ReThrowException Dart_ReThrowException_DL + +#define Dart_RootLibrary Dart_RootLibrary_DL + +#define Dart_ScopeAllocate Dart_ScopeAllocate_DL + +#define Dart_SetBooleanReturnValue Dart_SetBooleanReturnValue_DL + +#define Dart_SetDartLibrarySourcesKernel Dart_SetDartLibrarySourcesKernel_DL + +#define Dart_SetDoubleReturnValue Dart_SetDoubleReturnValue_DL + +#define Dart_SetEnvironmentCallback Dart_SetEnvironmentCallback_DL + +#define Dart_SetField Dart_SetField_DL + +#define Dart_SetIntegerReturnValue Dart_SetIntegerReturnValue_DL + +#define Dart_SetLibraryTagHandler Dart_SetLibraryTagHandler_DL + +#define Dart_SetMessageNotifyCallback Dart_SetMessageNotifyCallback_DL + +#define Dart_SetNativeInstanceField Dart_SetNativeInstanceField_DL + +#define Dart_SetPausedOnExit Dart_SetPausedOnExit_DL + +#define Dart_SetPausedOnStart Dart_SetPausedOnStart_DL + +#define Dart_SetPeer Dart_SetPeer_DL + +#define Dart_SetReturnValue Dart_SetReturnValue_DL + +#define Dart_SetRootLibrary Dart_SetRootLibrary_DL + +#define Dart_SetShouldPauseOnExit Dart_SetShouldPauseOnExit_DL + +#define Dart_SetShouldPauseOnStart Dart_SetShouldPauseOnStart_DL + +#define Dart_SetStickyError Dart_SetStickyError_DL + +#define Dart_SetWeakHandleReturnValue Dart_SetWeakHandleReturnValue_DL + +#define Dart_ShouldPauseOnExit Dart_ShouldPauseOnExit_DL + +#define Dart_ShouldPauseOnStart Dart_ShouldPauseOnStart_DL + +#define Dart_WaitForEvent Dart_WaitForEvent_DL + +#define Dart_WriteProfileToTimeline Dart_WriteProfileToTimeline_DL + +#define Dart_ShutdownIsolate Dart_ShutdownIsolate_DL + +#define Dart_StartProfiling Dart_StartProfiling_DL + +#define Dart_StopProfiling Dart_StopProfiling_DL + +#define Dart_StringGetProperties Dart_StringGetProperties_DL + +#define Dart_StringLength Dart_StringLength_DL + +#define Dart_StringStorageSize Dart_StringStorageSize_DL + +#define Dart_StringToCString Dart_StringToCString_DL + +#define Dart_StringToLatin1 Dart_StringToLatin1_DL + +#define Dart_StringToUTF16 Dart_StringToUTF16_DL + +#define Dart_StringToUTF8 Dart_StringToUTF8_DL + +#define Dart_ThreadDisableProfiling Dart_ThreadDisableProfiling_DL + +#define Dart_ThreadEnableProfiling Dart_ThreadEnableProfiling_DL + +#define Dart_ThrowException Dart_ThrowException_DL + +#define Dart_ToString Dart_ToString_DL + +#define Dart_True Dart_True_DL + +#define Dart_TypedDataAcquireData Dart_TypedDataAcquireData_DL + +#define Dart_TypedDataReleaseData Dart_TypedDataReleaseData_DL + +#define Dart_TypeDynamic Dart_TypeDynamic_DL + +#define Dart_TypeNever Dart_TypeNever_DL + +#define Dart_TypeToNonNullableType Dart_TypeToNonNullableType_DL + +#define Dart_TypeToNullableType Dart_TypeToNullableType_DL + +#define Dart_TypeVoid Dart_TypeVoid_DL + +#define Dart_VersionString Dart_VersionString_DL + + +#endif /* realm_flutter_h */ diff --git a/flutter/realm_flutter/ios/realm_flutter.podspec b/flutter/realm_flutter/ios/realm_flutter.podspec index 093e73c03..f4a48b266 100644 --- a/flutter/realm_flutter/ios/realm_flutter.podspec +++ b/flutter/realm_flutter/ios/realm_flutter.podspec @@ -1,23 +1,152 @@ + +current_dir = File.expand_path(__dir__) +puts "realm_flutter current directory: #{current_dir}" + +application_dir = File.expand_path("../../../../", current_dir) +puts "Application directory: #{application_dir}" + +project_file = File.expand_path("Runner.xcodeproj/project.pbxproj", application_dir) +puts "Project file: #{project_file}" + +if File.exist?(project_file) + puts "project file exists" +else + puts "project file DOES NOT exists" +end + +script_file = File.expand_path(".symlinks/plugins/realm_flutter/ios/scripts/xcode_backend.sh", application_dir) +puts "Scripts file: #{script_file}" + +if File.exist?(script_file) + puts "Scripts file exists" +else + puts "Scripts file DOES NOT exists" +end + +application_pod_file = File.expand_path("Podfile", application_dir) +puts "Application Podfile: #{application_pod_file}" +if File.exist?(application_pod_file) + puts "application_pod_file file exists" +else + puts "application_pod_file file DOES NOT exists" +end + + + + +# system("echo 123 | tr '3' '4'") + +# system("sed -i.bak \"s~flutter_additional_ios_build_settings(target)~mytarget=target#flutter_additional_ios_build_settings(target)#system(\'./.symlinks/plugins/realm_flutter/ios/scripts/prepare.sh #{project_file}\')~\" #{application_pod_file} | tr \'#\' \'\n\'") + +# system("sed -i.bak \"s~flutter_additional_ios_build_settings(target)~mytarget=target#flutter_additional_ios_build_settings(target)#system('./.symlinks/plugins/realm_flutter/ios/scripts/prepare.sh #{project_file}')~\" #{application_pod_file} | tr '#' '\n'") + +# system("sed -i.bak \"s~flutter_additional_ios_build_settings(target)~puts 'BLAGOEV'~\" #{application_pod_file}") + +# puts "PROJECT FILE CONTAINS=====================================================================================================================" +# system('cat', "#{project_file}") +# puts "PROJECT FILE CONTAINS=====================================================================================================================" + +system('sed', '-i.bak', "s~FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh~PROJECT_DIR/.symlinks/plugins/realm_flutter/ios/scripts/xcode_backend.sh~", project_file) +system('sed', '-i.bak', "s~(PROJECT_DIR)/Flutter~(PROJECT_DIR)/.symlinks/plugins/realm_flutter/ios/Frameworks/Flutter~", project_file) + +# puts "PROJECT FILE AFTER---------------------------------------------------------------------------------------------------------------------" +# system('cat', "#{project_file}") +# puts "PROJECT FILE AFTER---------------------------------------------------------------------------------------------------------------------" + +realm_dart_src_path = "realm-dart-src/src/" +realm_dart_extension_path = "#{realm_dart_src_path}/realm-dart-extension" +object_store_path = "#{realm_dart_src_path}/object-store" + + +# Create symlinks to every single source code file. Cocoapods can't use symlinks to directories + # -# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. +# To learn more about a Podspec see http:/guides.cocoapods.org/syntax/podspec.html. # Run `pod lib lint realm_flutter.podspec' to validate before publishing. # Pod::Spec.new do |s| - s.name = 'realm_flutter' - s.version = '0.0.1' - s.summary = 'A new flutter plugin project.' - s.description = <<-DESC + s.name = 'realm_flutter' + s.version = '0.0.1' + s.summary = 'A new flutter plugin project.' + s.description = <<-DESC A new flutter plugin project. DESC - s.homepage = 'http://example.com' - s.license = { :file => '../LICENSE' } - s.author = { 'Your Company' => 'email@example.com' } - s.source = { :path => '.' } - s.source_files = 'Classes/**/*' - s.dependency 'Flutter' - s.platform = :ios, '8.0' + s.homepage = 'http:/example.com' + s.license = { :file => '../LICENSE' } + s.author = { 'Your Company' => 'email@example.com' } + s.source = { :path => '.' } + s.source_files = 'Classes/**/*', + 'Classes/*.cpp', + 'Classes/*.mm', + 'realm-dart-src/src/realm-dart-extension/*.cpp', + 'realm-dart-src/src/realm-dart-extension/*.hpp', + 'realm-dart-src/src/realm-dart-extension/dart/dart_init.cpp', + 'realm-dart-src/src/realm-dart-extension/dart/dart_types.cpp', + 'realm-dart-src/src/realm-dart-extension/dart/*.hpp', + # 'realm-dart-src/src/realm-dart-extension/dart/dart_types.cpp', + # 'realm-dart-src/src/realm-dart-extension/dart/platform.cpp', + 'realm-dart-src/src/realm-dart-extension/realm-js-common/*.cpp', + 'realm-dart-src/src/realm-dart-extension/realm-js-common/*.hpp', + 'realm-dart-src/src/object-store/src/impl/*.cpp', + 'realm-dart-src/src/object-store/src/impl/*.hpp', + 'realm-dart-src/src/object-store/src/impl/apple/*.cpp', + 'realm-dart-src/src/object-store/src/impl/apple/*.hpp', + # 'realm-dart-src/src/object-store/src/impl/collection_notifier.cpp', + # 'realm-dart-src/src/object-store/src/impl/list_notifier.cpp', + # 'realm-dart-src/src/object-store/src/impl/object_notifier.cpp', + # 'realm-dart-src/src/object-store/src/impl/realm_coordinator.cpp', + # 'realm-dart-src/src/object-store/src/impl/results_notifier.cpp', + # 'realm-dart-src/src/object-store/src/impl/transact_log_handler.cpp', + # 'realm-dart-src/src/object-store/src/impl/weak_realm_notifier.cpp', + # 'realm-dart-src/src/object-store/src/impl/apple/external_commit_helper.cpp', + # 'realm-dart-src/src/object-store/src/impl/apple/keychain_helper.cpp', + 'realm-dart-src/src/object-store/src/util/*.cpp', + 'realm-dart-src/src/object-store/src/util/*.hpp', + # 'realm-dart-src/src/object-store/src/util/scheduler.cpp', + 'realm-dart-src/src/object-store/src/*.cpp', + 'realm-dart-src/src/object-store/src/*.hpp', + # 'realm-dart-src/src/object-store/src/collection_notifications.cpp', + # 'realm-dart-src/src/object-store/src/index_set.cpp', + # 'realm-dart-src/src/object-store/src/list.cpp', + # 'realm-dart-src/src/object-store/src/object_schema.cpp', + # 'realm-dart-src/src/object-store/src/object_store.cpp', + # 'realm-dart-src/src/object-store/src/object.cpp', + # 'realm-dart-src/src/object-store/src/object_changeset.cpp', + # 'realm-dart-src/src/object-store/src/results.cpp', + # 'realm-dart-src/src/object-store/src/schema.cpp', + # 'realm-dart-src/src/object-store/src/shared_realm.cpp', + # 'realm-dart-src/src/object-store/src/thread_safe_reference.cpp' + + s.public_header_files = 'Classes/RealmFlutterPlugin.h' + s.ios.vendored_libraries = 'realm-dart-src/src/vendor-include/realm-ios/librealm-ios.a', + 'realm-dart-src/src/vendor-include/realm-ios/librealm-parser-ios.a' + s.dependency 'Flutter' + s.platform = :ios, '8.0' + s.prepare_command = "./scripts/prepare.sh #{project_file}" + # Flutter.framework does not contain a i386 slice. Only x86_64 simulators are supported. - s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS[sdk=iphonesimulator*]' => 'x86_64' } - s.swift_version = '5.0' -end + s.swift_version = '5.0' + s.compiler_flags = '-DREALM_HAVE_CONFIG -DFLUTTER' + s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS[sdk=iphonesimulator*]' => 'x86_64', + 'FRAMEWORK_SEARCH_PATHS' => '$(PROJECT_DIR)/.symlinks/plugins/realm_flutter/ios/Frameworks/Flutter', + 'LIBRARY_SEARCH_PATHS' => '$(PROJECT_DIR)/.symlinks/plugins/realm_flutter/ios/Frameworks/Flutter', + 'CLANG_CXX_LANGUAGE_STANDARD' => 'c++17', + 'CLANG_CXX_LIBRARY' => 'libc++', + 'CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF' => 'NO', + 'CLANG_WARN_DOCUMENTATION_COMMENTS' => 'NO', + 'CLANG_WARN_STRICT_PROTOTYPES' => 'No', + 'CLANG_WARN_INT_CONVERSION' => 'No', + 'OTHER_CPLUSPLUSFLAGS[arch=armv7]' => '-fno-aligned-new', + 'HEADER_SEARCH_PATHS' => [ + '"$(PODS_TARGET_SRCROOT)/src/"', + '"$(PODS_TARGET_SRCROOT)/realm-dart-src/src/object-store/src/"', + '"$(PODS_TARGET_SRCROOT)/realm-dart-src/src/object-store/external/json/"', + '"$(PODS_TARGET_SRCROOT)/realm-dart-src/src/vendor-include/realm-ios/include"', + '"$(PODS_TARGET_SRCROOT)/realm-dart-src/src/vendor-include/realm-ios/include/realm/"' + ].join(' ') + } + s.user_target_xcconfig = { 'CLANG_WARN_DOCUMENTATION_COMMENTS' => 'No', + 'CLANG_WARN_STRICT_PROTOTYPES' => 'No', + 'CLANG_WARN_INT_CONVERSION' => 'No', } +end \ No newline at end of file diff --git a/flutter/realm_flutter/ios/scripts/prepare.sh b/flutter/realm_flutter/ios/scripts/prepare.sh new file mode 100755 index 000000000..8edf9127c --- /dev/null +++ b/flutter/realm_flutter/ios/scripts/prepare.sh @@ -0,0 +1,32 @@ +echo "prepare.sh called in $(pwd)" +echo "patching project file $1" + + +EchoError() { + echo "$@" 1>&2 +} + +if [[ $# == 0 ]]; then + EchoError "prepare.sh script requires a argument - the path to the xcode project file" + exit -1 +fi + +AssertExists() { + if [[ ! -e "$1" ]]; then + if [[ -h "$1" ]]; then + EchoError "The path $1 is a symlink to a path that does not exist" + else + EchoError "The path $1 does not exist" + fi + exit -1 + fi + return 0 +} + +AssertExists $1 + +# system('sed', '-i.bak', "s~FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh~PROJECT_DIR/.symlinks/plugins/realm_flutter/ios/scripts/xcode_backend.sh~", project_file) +# system('sed', '-i.bak', "s~(PROJECT_DIR)/Flutter~(PROJECT_DIR)/.symlinks/plugins/realm_flutter/ios/Frameworks/Flutter~", project_file) + +sed -i.bak "s~FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh~PROJECT_DIR/.symlinks/plugins/realm_flutter/ios/scripts/xcode_backend.sh~" $1 +sed -i.bak "s~(PROJECT_DIR)/Flutter~(PROJECT_DIR)/.symlinks/plugins/realm_flutter/ios/Frameworks/Flutter~" $1 \ No newline at end of file diff --git a/flutter/realm_flutter/ios/scripts/xcode_backend.sh b/flutter/realm_flutter/ios/scripts/xcode_backend.sh new file mode 100755 index 000000000..5cb63c677 --- /dev/null +++ b/flutter/realm_flutter/ios/scripts/xcode_backend.sh @@ -0,0 +1,395 @@ +#!/usr/bin/env bash +# Copyright 2014 The Flutter Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# Exit on error +set -e + +RunCommand() { + if [[ -n "$VERBOSE_SCRIPT_LOGGING" ]]; then + echo "♦ $*" + fi + "$@" + return $? +} + +# When provided with a pipe by the host Flutter build process, output to the +# pipe goes to stdout of the Flutter build process directly. +StreamOutput() { + if [[ -n "$SCRIPT_OUTPUT_STREAM_FILE" ]]; then + echo "$1" > $SCRIPT_OUTPUT_STREAM_FILE + fi +} + +EchoError() { + echo "$@" 1>&2 +} + +AssertExists() { + if [[ ! -e "$1" ]]; then + if [[ -h "$1" ]]; then + EchoError "The path $1 is a symlink to a path that does not exist" + else + EchoError "The path $1 does not exist" + fi + exit -1 + fi + return 0 +} + +ParseFlutterBuildMode() { + # Use FLUTTER_BUILD_MODE if it's set, otherwise use the Xcode build configuration name + # This means that if someone wants to use an Xcode build config other than Debug/Profile/Release, + # they _must_ set FLUTTER_BUILD_MODE so we know what type of artifact to build. + local build_mode="$(echo "${FLUTTER_BUILD_MODE:-${CONFIGURATION}}" | tr "[:upper:]" "[:lower:]")" + + case "$build_mode" in + *release*) build_mode="release";; + *profile*) build_mode="profile";; + *debug*) build_mode="debug";; + *) + EchoError "========================================================================" + EchoError "ERROR: Unknown FLUTTER_BUILD_MODE: ${build_mode}." + EchoError "Valid values are 'Debug', 'Profile', or 'Release' (case insensitive)." + EchoError "This is controlled by the FLUTTER_BUILD_MODE environment variable." + EchoError "If that is not set, the CONFIGURATION environment variable is used." + EchoError "" + EchoError "You can fix this by either adding an appropriately named build" + EchoError "configuration, or adding an appropriate value for FLUTTER_BUILD_MODE to the" + EchoError ".xcconfig file for the current build configuration (${CONFIGURATION})." + EchoError "========================================================================" + exit -1;; + esac + echo "${build_mode}" +} + +BuildApp() { + local project_path="${SOURCE_ROOT}/.." + if [[ -n "$FLUTTER_APPLICATION_PATH" ]]; then + project_path="${FLUTTER_APPLICATION_PATH}" + fi + + local target_path="lib/main.dart" + if [[ -n "$FLUTTER_TARGET" ]]; then + target_path="${FLUTTER_TARGET}" + fi + + local derived_dir="${SOURCE_ROOT}/Flutter" + # if [[ -e "${project_path}/.ios" ]]; then + # derived_dir="${project_path}/.ios/Flutter" + # fi + + local bundle_sksl_path="" + if [[ -n "$BUNDLE_SKSL_PATH" ]]; then + bundle_sksl_path="-iBundleSkSLPath=${BUNDLE_SKSL_PATH}" + fi + + # Default value of assets_path is flutter_assets + local assets_path="flutter_assets" + # The value of assets_path can set by add FLTAssetsPath to + # AppFrameworkInfo.plist. + if FLTAssetsPath=$(/usr/libexec/PlistBuddy -c "Print :FLTAssetsPath" "${derived_dir}/AppFrameworkInfo.plist" 2>/dev/null); then + if [[ -n "$FLTAssetsPath" ]]; then + assets_path="${FLTAssetsPath}" + fi + fi + + # Use FLUTTER_BUILD_MODE if it's set, otherwise use the Xcode build configuration name + # This means that if someone wants to use an Xcode build config other than Debug/Profile/Release, + # they _must_ set FLUTTER_BUILD_MODE so we know what type of artifact to build. + local build_mode="$(ParseFlutterBuildMode)" + local artifact_variant="unknown" + case "$build_mode" in + release ) artifact_variant="ios-release";; + profile ) artifact_variant="ios-profile";; + debug ) artifact_variant="ios";; + esac + + # Warn the user if not archiving (ACTION=install) in release mode. + if [[ "$ACTION" == "install" && "$build_mode" != "release" ]]; then + echo "warning: Flutter archive not built in Release mode. Ensure FLUTTER_BUILD_MODE \ +is set to release or run \"flutter build ios --release\", then re-run Archive from Xcode." + fi + + local framework_path="${FLUTTER_ROOT}/bin/cache/artifacts/engine/${artifact_variant}" + local flutter_engine_flag="" + local local_engine_flag="" + local flutter_framework="${framework_path}/Flutter.framework" + local flutter_podspec="${framework_path}/Flutter.podspec" + + if [[ -n "$FLUTTER_ENGINE" ]]; then + flutter_engine_flag="--local-engine-src-path=${FLUTTER_ENGINE}" + fi + + if [[ -n "$LOCAL_ENGINE" ]]; then + if [[ $(echo "$LOCAL_ENGINE" | tr "[:upper:]" "[:lower:]") != *"$build_mode"* ]]; then + EchoError "========================================================================" + EchoError "ERROR: Requested build with Flutter local engine at '${LOCAL_ENGINE}'" + EchoError "This engine is not compatible with FLUTTER_BUILD_MODE: '${build_mode}'." + EchoError "You can fix this by updating the LOCAL_ENGINE environment variable, or" + EchoError "by running:" + EchoError " flutter build ios --local-engine=ios_${build_mode}" + EchoError "or" + EchoError " flutter build ios --local-engine=ios_${build_mode}_unopt" + EchoError "========================================================================" + exit -1 + fi + local_engine_flag="--local-engine=${LOCAL_ENGINE}" + flutter_framework="${FLUTTER_ENGINE}/out/${LOCAL_ENGINE}/Flutter.framework" + flutter_podspec="${FLUTTER_ENGINE}/out/${LOCAL_ENGINE}/Flutter.podspec" + fi + + local bitcode_flag="" + if [[ "$ENABLE_BITCODE" == "YES" ]]; then + bitcode_flag="true" + fi + + flutter_framework="${project_path}/ios/.symlinks/plugins/realm_flutter/ios/Frameworks/Flutter/Flutter.framework" + flutter_podspec="${project_path}/ios/.symlinks/plugins/realm_flutter/ios/Frameworks/Flutter/Flutter.podspec" + + # TODO(jonahwilliams): move engine copying to build system. + # if [[ -e "${project_path}/.ios" ]]; then + # echo "skipped" + # # RunCommand rm -rf -- "${derived_dir}/engine" + # # mkdir "${derived_dir}/engine" + # # RunCommand cp -r -- "${flutter_podspec}" "${derived_dir}/engine" + # # RunCommand cp -r -- "${flutter_framework}" "${derived_dir}/engine" + # else + # echo "skipped" + RunCommand rm -rf -- "${derived_dir}/Flutter.framework" + RunCommand cp -R "${flutter_framework}/" "${derived_dir}/Flutter.framework" + # RunCommand cp -- "${flutter_podspec}" "${derived_dir}" + # RunCommand cp -r -- "${flutter_framework}" "${derived_dir}" + # fi + + + + AssertExists "${flutter_framework}" + AssertExists "${flutter_podspec}" + + RunCommand pushd "${project_path}" > /dev/null + + local verbose_flag="" + if [[ -n "$VERBOSE_SCRIPT_LOGGING" ]]; then + verbose_flag="--verbose" + fi + + local performance_measurement_option="" + if [[ -n "$PERFORMANCE_MEASUREMENT_FILE" ]]; then + performance_measurement_option="--performance-measurement-file=${PERFORMANCE_MEASUREMENT_FILE}" + fi + + local code_size_directory="" + if [[ -n "$CODE_SIZE_DIRECTORY" ]]; then + code_size_directory="-dCodeSizeDirectory=${CODE_SIZE_DIRECTORY}" + fi + + RunCommand "${FLUTTER_ROOT}/bin/flutter" \ + ${verbose_flag} \ + ${flutter_engine_flag} \ + ${local_engine_flag} \ + assemble \ + --output="${derived_dir}/" \ + ${performance_measurement_option} \ + -dTargetPlatform=ios \ + -dTargetFile="${target_path}" \ + -dBuildMode=${build_mode} \ + -dIosArchs="${ARCHS}" \ + -dSplitDebugInfo="${SPLIT_DEBUG_INFO}" \ + -dTreeShakeIcons="${TREE_SHAKE_ICONS}" \ + -dTrackWidgetCreation="${TRACK_WIDGET_CREATION}" \ + -dDartObfuscation="${DART_OBFUSCATION}" \ + -dEnableBitcode="${bitcode_flag}" \ + ${bundle_sksl_path} \ + ${code_size_directory} \ + --ExtraGenSnapshotOptions="${EXTRA_GEN_SNAPSHOT_OPTIONS}" \ + --DartDefines="${DART_DEFINES}" \ + --ExtraFrontEndOptions="${EXTRA_FRONT_END_OPTIONS}" \ + "${build_mode}_ios_bundle_flutter_assets" + + if [[ $? -ne 0 ]]; then + EchoError "Failed to package ${project_path}." + exit -1 + fi + StreamOutput "done" + StreamOutput " └─Compiling, linking and signing..." + + RunCommand popd > /dev/null + + echo "Project ${project_path} built and packaged successfully." + return 0 +} + +# Returns the CFBundleExecutable for the specified framework directory. +GetFrameworkExecutablePath() { + local framework_dir="$1" + + local plist_path="${framework_dir}/Info.plist" + local executable="$(defaults read "${plist_path}" CFBundleExecutable)" + echo "${framework_dir}/${executable}" +} + +# Destructively thins the specified executable file to include only the +# specified architectures. +LipoExecutable() { + local executable="$1" + shift + # Split $@ into an array. + read -r -a archs <<< "$@" + + # Extract architecture-specific framework executables. + local all_executables=() + for arch in "${archs[@]}"; do + local output="${executable}_${arch}" + local lipo_info="$(lipo -info "${executable}")" + if [[ "${lipo_info}" == "Non-fat file:"* ]]; then + if [[ "${lipo_info}" != *"${arch}" ]]; then + echo "Non-fat binary ${executable} is not ${arch}. Running lipo -info:" + echo "${lipo_info}" + exit 1 + fi + else + if lipo -output "${output}" -extract "${arch}" "${executable}"; then + all_executables+=("${output}") + else + echo "Failed to extract ${arch} for ${executable}. Running lipo -info:" + lipo -info "${executable}" + exit 1 + fi + fi + done + + # Generate a merged binary from the architecture-specific executables. + # Skip this step for non-fat executables. + if [[ ${#all_executables[@]} > 0 ]]; then + local merged="${executable}_merged" + lipo -output "${merged}" -create "${all_executables[@]}" + + cp -f -- "${merged}" "${executable}" > /dev/null + rm -f -- "${merged}" "${all_executables[@]}" + fi +} + +# Destructively thins the specified framework to include only the specified +# architectures. +ThinFramework() { + local framework_dir="$1" + shift + + local executable="$(GetFrameworkExecutablePath "${framework_dir}")" + LipoExecutable "${executable}" "$@" +} + +ThinAppFrameworks() { + local app_path="${TARGET_BUILD_DIR}/${WRAPPER_NAME}" + local frameworks_dir="${app_path}/Frameworks" + + [[ -d "$frameworks_dir" ]] || return 0 + find "${app_path}" -type d -name "*.framework" | while read framework_dir; do + ThinFramework "$framework_dir" "$ARCHS" + done +} + +# Adds the App.framework as an embedded binary and the flutter_assets as +# resources. +EmbedFlutterFrameworks() { + local project_path="${SOURCE_ROOT}/.." + if [[ -n "$FLUTTER_APPLICATION_PATH" ]]; then + project_path="${FLUTTER_APPLICATION_PATH}" + fi + + # # Prefer the hidden .ios folder, but fallback to a visible ios folder if .ios + # # doesn't exist. + # local flutter_ios_out_folder="${project_path}/.ios/Flutter" + # local flutter_ios_engine_folder="${project_path}/.ios/Flutter/engine" + #local flutter_ios_engine_folder="${project_path}/.ios/Flutter/engine" + # if [[ ! -d ${flutter_ios_out_folder} ]]; then + flutter_ios_out_folder="${project_path}/ios/Flutter" + flutter_ios_engine_folder="${project_path}/ios/Flutter" + # fi + flutter_framework="${project_path}/ios/.symlinks/plugins/realm_flutter/ios/Frameworks/Flutter/Flutter.framework" + echo "flutter_framework PATTH: ${flutter_framework}" + AssertExists "${flutter_ios_out_folder}" + + # Embed App.framework from Flutter into the app (after creating the Frameworks directory + # if it doesn't already exist). + local xcode_frameworks_dir="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + RunCommand mkdir -p -- "${xcode_frameworks_dir}" + + RunCommand rsync -av --delete "${flutter_ios_out_folder}/App.framework" "${xcode_frameworks_dir}" + + # Embed the actual Flutter.framework that the Flutter app expects to run against, + # which could be a local build or an arch/type specific build. + + # Copy Xcode behavior and don't copy over headers or modules. + # RunCommand rsync -av --delete --filter "- .DS_Store/" --filter "- Headers/" --filter "- Modules/" "${flutter_ios_engine_folder}/Flutter.framework" "${xcode_frameworks_dir}/" + RunCommand rsync -av --delete --filter "- .DS_Store/" --filter "- Headers/" --filter "- Modules/" "${flutter_framework}" "${xcode_frameworks_dir}/" + if [[ "$ACTION" != "install" || "$ENABLE_BITCODE" == "NO" ]]; then + # Strip bitcode from the destination unless archiving, or if bitcode is disabled entirely. + # RunCommand "${DT_TOOLCHAIN_DIR}"/usr/bin/bitcode_strip "${flutter_ios_engine_folder}/Flutter.framework/Flutter" -r -o "${xcode_frameworks_dir}/Flutter.framework/Flutter" + RunCommand "${DT_TOOLCHAIN_DIR}"/usr/bin/bitcode_strip "${flutter_framework}/Flutter" -r -o "${xcode_frameworks_dir}/Flutter.framework/Flutter" + fi + + # Sign the binaries we moved. + if [[ -n "${EXPANDED_CODE_SIGN_IDENTITY:-}" ]]; then + RunCommand codesign --force --verbose --sign "${EXPANDED_CODE_SIGN_IDENTITY}" -- "${xcode_frameworks_dir}/App.framework/App" + RunCommand codesign --force --verbose --sign "${EXPANDED_CODE_SIGN_IDENTITY}" -- "${xcode_frameworks_dir}/Flutter.framework/Flutter" + fi + + AddObservatoryBonjourService +} + +# Add the observatory publisher Bonjour service to the produced app bundle Info.plist. +AddObservatoryBonjourService() { + local build_mode="$(ParseFlutterBuildMode)" + # Debug and profile only. + if [[ "${build_mode}" == "release" ]]; then + return + fi + local built_products_plist="${BUILT_PRODUCTS_DIR}/${INFOPLIST_PATH}" + + if [[ ! -f "${built_products_plist}" ]]; then + EchoError "error: ${INFOPLIST_PATH} does not exist. The Flutter \"Thin Binary\" build phase must run after \"Copy Bundle Resources\"." + exit -1 + fi + # If there are already NSBonjourServices specified by the app (uncommon), insert the observatory service name to the existing list. + if plutil -extract NSBonjourServices xml1 -o - "${built_products_plist}"; then + RunCommand plutil -insert NSBonjourServices.0 -string "_dartobservatory._tcp" "${built_products_plist}" + else + # Otherwise, add the NSBonjourServices key and observatory service name. + RunCommand plutil -insert NSBonjourServices -json "[\"_dartobservatory._tcp\"]" "${built_products_plist}" + fi + + # Don't override the local network description the Flutter app developer specified (uncommon). + # This text will appear below the "Your app would like to find and connect to devices on your local network" permissions popup. + if ! plutil -extract NSLocalNetworkUsageDescription xml1 -o - "${built_products_plist}"; then + RunCommand plutil -insert NSLocalNetworkUsageDescription -string "Allow Flutter tools on your computer to connect and debug your application. This prompt will not appear on release builds." "${built_products_plist}" + fi +} + +EmbedAndThinFrameworks() { + EmbedFlutterFrameworks + ThinAppFrameworks +} + +# Main entry point. +if [[ $# == 0 ]]; then + # Named entry points were introduced in Flutter v0.0.7. + EchoError "error: Your Xcode project is incompatible with this version of Flutter. Run \"rm -rf ios/Runner.xcodeproj\" and \"flutter create .\" to regenerate." + exit -1 +else + case $1 in + "build") + BuildApp ;; + "thin") + ThinAppFrameworks ;; + "embed") + EmbedFlutterFrameworks ;; + "embed_and_thin") + EmbedAndThinFrameworks ;; + "test_observatory_bonjour_service") + # Exposed for integration testing only. + AddObservatoryBonjourService ;; + esac +fi diff --git a/src/realm-dart-extension/dart/dart_types.cpp b/src/realm-dart-extension/dart/dart_types.cpp index d51552571..3fb0e9f04 100644 --- a/src/realm-dart-extension/dart/dart_types.cpp +++ b/src/realm-dart-extension/dart/dart_types.cpp @@ -15,6 +15,7 @@ // limitations under the License. // //////////////////////////////////////////////////////////////////////////// +#pragma clang diagnostic ignored "-Wdocumentation" #include #include diff --git a/src/vendor-include/realm-ios/doc/realm/CHANGELOG.md b/src/vendor-include/realm-ios/doc/realm/CHANGELOG.md new file mode 100644 index 000000000..09ef6a5d3 --- /dev/null +++ b/src/vendor-include/realm-ios/doc/realm/CHANGELOG.md @@ -0,0 +1,5397 @@ +# 6.0.4 Release notes + +### Fixed +* It was not possible to make client resync if a table contained binary data. ([#3619](https://github.com/realm/realm-core/issues/3619), v6.0.0-alpha.0) + +---------------------------------------------- + +# 6.0.3 Release notes + +### Fixed +* You may under certain conditions get a "Key not found" exception when creating an object. ([#3610](https://github.com/realm/realm-core/issues/3610), 6.0.0-alpha-0) + +---------------------------------------------- + +# 6.0.2 Release notes + +### Enhancements +* Use search index to speed up Query::count, Query::find_all and generic aggregate function. + +### Fixed +* None. + +----------- + +### Internals +* Don't force downstream components to look for OpenSSL on Windows. + +---------------------------------------------- + +# 6.0.1 Release notes + +### Fixed +* None. + +----------- + +### Internals +* Build targets are now RealmCore::Storage and RealmCore::QueryParser +* OpenSSL library will be downloaded automatically on Linux and Android + +---------------------------------------------- + +# 6.0.0 Release notes + +### Fixed since 6.0.0-beta.3 +* Fixed float and double maximum queries when all values were less or equal to zero.. +* Using case insensitive search on a primary key string column would crash. ([#3564](https://github.com/realm/realm-core/issues/3564), since v6.0.0-alpha.25) + +### Internals since 6.0.0-beta.3 +* Upgrade OpenSSL for Android to version 1.1.1b. +* Upgrade the NDK to version 21. +* Removed support for ARMv5 and MIPS from Android. This is a consequence of the new NDK being used. + +Wrap up of the changes done from v6.0.0.alpha0 to v6.0.0-beta.3 compared to v5.23.7: + +### Enhancements +* `Table::contains_unique_values(ColKey) -> bool` function added +* Ability to stream into an GlobalKey added. +* Table iterator to support random access. If the object that iterator points to is deleted, the iterator must be advanced or renewed before used for table access. +* Allow DB::close to release tethered versions. +* Further reduction of the use of virtual address space. ([#3392](https://github.com/realm/realm-core/pull/3392)) +* min, max, sum and avg functions added as virtual members of ConstLstBase. Makes it possible to + get the values without knowing the exact type of list. +* pk handling enhanched so that it can be used by OS +* Ability to get a general pointer to list object added to ConstObj +* All ObjectID handling functionality has been moved from Sync to Core. +* The primary key column concept is now handled in Core in a way consistent with Sync and ObjectStore. This means that the corresponding code can be removed in those subsystems. +* Creating tables and objects can now be done entirely using the Core interface. All sync::create_table...() and sync::create_object...() functions now map directly to a core function. +* Ability to get/set Mixed on ConstObj/Obj. +* Providing option to supply initial values when creating an object. This can be used as an optimization when some columns have a search index. Then you don't have to first insert the default value in the index and subsequently the real value. + +### Fixed +* Fixed assert in SlabAlloc::allocate_block() which could falsely trigger when requesting an allocation that + would be slightly smaller than the underlying free block. ([3490](https://github.com/realm/realm-core/issues/3490)) +* Queries can be built without mutating the Table object.([#237](https://github.com/realm/realm-core-private/issues/237), since v1.0.0) + +### Breaking changes +* We now require uniqieness on table names. +* Implicit conversion between Table* and TableRef is removed. It you want to get a raw Table* from a TableRef, you will + have to call TableRef::unchecked_ptr(). Several API functions now take a ConstTableRef instead of a 'const Table*'. +* DB objects are now heap allocated and accessed through a DBRef. You must create a DB using static DB::create() function. +* SharedGroup class removed. New DB class added allowing you to specify the Realm file + only once regardless of the number of threads. +* New Transaction class added, inheriting from Group. Transactions are produced by DB. +* TransactionRef added to provide reference to a Transaction. This is a std::shared_ptr. +* Old export_for_handover/import_from_handover has been removed. Handover is now direct + from one transaction to another, using a new method Transaction::import_copy_of(). +* 'begin' argument removed from Query::count(); +* Table::optimize replaced with explicit Table::enumerate_string_column. It will enumerate + column unconditionally. +* TableView::num_attached_rows() no longer available. get(), front, back()... and similar + functions will throw `InvalidKey` if the referenced object has been deleted. +* Removed the ability to sort a linklist according to some property of the target table. This + operation is not supported by the sync protocol and allegedly not used in the bindings. +* Moved `get_uncommitted_changes()` from `History` class to `Replication` class. This removes the + need to call TrivialReplication::get_uncommitted_changes from a decendant of History. +* Column identifier changed from size_t to ColKey. This identifier is subsequently + used when accessing/mutating the data and when making queries. +* Table identifiers changed from size_t to TableKey. +* Row interface on Table removed. All access to data goes through the new Obj + interface. Obj objects are created with an ObjKey identifier, which subsequently + is used to get access to and optionally delete the object. +* Support for sub-tables removed. The feature was only used to implement support for + array-of-primitives. This is now implemented by a more specific List class. +* Descriptor class removed. No longer needed as we don't have sub-tables. +* Mixed column no longer supported. Will be revived at a later point in time. +* LinkView is replaced by the more general list interface +* Limiting parameters removed from Query aggregate functions (start, end, limit) +* Table::get_range_view() has been removed. +* Table::merge_rows() not supported. Not needed anymore. +* Ref-counted freestanding tables can no longer be created (by Table::create) +* OldDateTime is no longer supported +* An old database cannot be opened without being updated to the new file version. + This implies that an old database cannot be opened in read-only, as read-only + prevents updating. +* Only file format versions from 6 and onwards can be opened (realm core v2.0.0) + +----------- + +### Internals +* ObjectID renamed to GlobalKey +* Table is now rebuilt when a string property is selected as new primary key. +* pk table removed. Primary key column is stored in Table structure. +* Search index is not added to primary key string columns. Will compute key directly from primary key value. +* ConstTableView::get_dependency_versions() is now public. Needed by realm-object-store. +* ConstObj::to_string() added. Mostly to be used during debugging. +* SortDescriptor::is_ascending() added. Potentially to be used by OS. +* Class ObjectId is moved over from Sync. +* Column keys cannot be used across tables. If a key is obtained from one table, it can only be used in relation to that table. +* realm-browser now able to read Core-6 files and print values for even more columns +* All read transactions will have a separate history object. Read transactions + use the object stored in the Replication object. +* Major simplifications and optimizations to management of memory mappings. +* Speed improvement for Sort(). + +---------------------------------------------- + +# 6.0.0-beta.3 Release notes + +### Enhancements +* `Table::contains_unique_values(ColKey) -> bool` function added + +### Fixed +* Fixed an assertion failure when rebuilding a table with a null primary key, since 6.0.0-beta.2 ([#3528](https://github.com/realm/realm-core/issues/3528)). + +### Breaking changes +* We now require uniqieness on table names. + +---------------------------------------------- + +# 6.0.0-beta.2 Release notes + +Includes changes introduced by v5.23.7 + +### Outstanding issues. +* Links lost during Table rebuild.([RCORE-229](https://jira.mongodb.org/browse/RCORE-229)) + +### Fixed +* None. + +----------- + +### Internals +* ObjectID renamed to GlobalKey +* Table is now rebuilt when a string property is selected as new primary key. + +---------------------------------------------- + +# 6.0.0-beta.1 Release notes + +This release was never published + +---------------------------------------------- + +# 6.0.0-beta.0 Release notes + +### Enhancements +* Ability to stream into an ObjectId added. + +### Fixed +* Fixed assert in SlabAlloc::allocate_block() which could falsely trigger when requesting an allocation that + would be slightly smaller than the underlying free block. ([3490](https://github.com/realm/realm-core/issues/3490)) + +### Breaking changes +* Implicit conversion between Table* and TableRef is removed. It you want to get a raw Table* from a TableRef, you will + have to call TableRef::unchecked_ptr(). Several API functions now take a ConstTableRef instead of a 'const Table*'. + +---------------------------------------------- + +# 6.0.0-alpha.27 Release notes + +### Enhancements +* Table iterator to support random access. If the object that iterator points to is deleted, the iterator must be advanced or renewed before used for table access. +* Allow DB::close to release tethered versions. + +### Fixed +* The way the Table iterator was used in ObjectStore could result in an exception if the element recently accessed was deleted. ([#3482](https://github.com/realm/realm-core/issues/3482), since 6.0.0-alpha.0) + +---------------------------------------------- + +# 6.0.0-alpha.26 Release notes + +This release was never published + +---------------------------------------------- + +# 6.0.0-alpha.25 Release notes + +### Fixed +* Upgrading a realm file with a table with no columns would fail ([#3470](https://github.com/realm/realm-core/issues/3470)) + +### Breaking changes +* Table file layout changed. Will not be able to read files produced by ealier 6.0.0 alpha versions. + +----------- + +### Internals +* pk table removed. Primary key column is stored in Table structure. +* Search index is not added to primary key string columns. Will compute key directly from primary key value. + +---------------------------------------------- + +# 6.0.0-alpha.24 Release notes + +### Enhancements +* Improve performance of looking up objects in a Table by index. ([#3423](https://github.com/realm/realm-core/pull/3423)) +* Cascade notifications are sent in batches. Enhances performance of KVO notifications. + +### Fixed +* macOS binaries were built with the incorrect deployment target (10.14 rather than 10.9). ([Cocoa #6299](https://github.com/realm/realm-cocoa/issues/6299), since 5.23.4). +* Upgrading medium sized string columns to new file format would not be done correctly. +* BPlusTree used in lists and TableViews could in some rare cases give wrong values or crash. ([#3449](https://github.com/realm/realm-core/issues/3449)) + +---------------------------------------------- + +# 6.0.0-alpha.23 Release notes + +### Internals +* ConstTableView::get_dependency_versions() is now public. Needed by realm-object-store. + +---------------------------------------------- + +# 6.0.0-alpha.22 Release notes + +### Fixed +* Opening a file where the size is a multiplum of 64 MB will crash. ([#3418](https://github.com/realm/realm-core/issues/3418), since v6.0.0-alpha.0) +* If a Replication object was deleted before the DB object the program would crash. ([#3416](https://github.com/realm/realm-core/issues/3416), since v6.0.0-alpha.0) +* Migration of a nullable list would fail. +* Using Query::and_query could crash. (Used by List::filter in realm-object-store) + +----------- + +### Internals +* Several performance improvements. + +---------------------------------------------- + +# 6.0.0-alpha.21 Release notes + +### Internals +* Reverting the changes made in the previous release. The fix will be done in Sync instead + +---------------------------------------------- + +# 6.0.0-alpha.20 Release notes + +### Internals +* Workaround added for the issues around ArrayInsert in the legacy + sync implementation. + +---------------------------------------------- + +# 6.0.0-alpha.19 Release notes + +### Enhancements +* Further reduction of the use of virtual address space. ([#3392](https://github.com/realm/realm-core/pull/3392)) + +### Fixed +* Creating an equal query on a string column with an index confined by a list view would give wrong results ([#333](https://github.com/realm/realm-core-private/issues/333), since v6.0.0-alpha.0) +* Setting a null on a link would not get replicated. ([#334](https://github.com/realm/realm-core-private/issues/334), since v6.0.0-alpha.0) + +----------- + +### Internals +* A workaround for the problem that values inserted/added on a list-of-primitives were not stored on the server has been added for strings. + +---------------------------------------------- + +# 6.0.0-alpha.18 Release notes + +### Enhancements +* We now reserve way less virtual address space. ([#3384](https://github.com/realm/realm-core/pull/3384)) + +----------- + +### Internals +* You can now do sort as part of distinct on Lists. + +---------------------------------------------- + +# 6.0.0-alpha.17 Release notes + +### Fixed +* There would be a crash if you tried to import a query with a detached linkview into a new transaction ([#328](https://github.com/realm/realm-core-private/issues/328), since v6.0.0-alpha.0) +* Queries can be built without mutating the Table object.([#237](https://github.com/realm/realm-core-private/issues/237), since v1.0.0) + +----------- + +### Internals +* sort() and distinct() added to the List interface + +---------------------------------------------- + +# 6.0.0-alpha.16 Release notes + +### Fixed +* Clearing a table with links to itself could sometimes result in a crash. + ([#324](https://github.com/realm/realm-core-private/issues/324)) + +----------- + +### Internals +* Fixes unittest Shared_WaitForChange which could deadlock. + ([#3346] https://github.com/realm/realm-core/issues/3346) + +---------------------------------------------- + +# 6.0.0-alpha.15 Release notes + +### Enhancements +* min, max, sum and avg functions added as virtual members of ConstLstBase. Makes it possible to + get the values without knowing the exact type of list. + +### Fixed +* Program would crash if a primary key column is set in a transaction that is subsequently rolled back. + ([#814](https://github.com/realm/realm-object-store/issues/814), since 6.0.0-alpha.11) +* Creating new objects in a file with InRealm history and migrated from file format 9 would fail. + ([#3334](https://github.com/realm/realm-core/issues/3334), since 6.0.0-alpha.8) + +---------------------------------------------- + +# 6.0.0-alpha.14 Release notes + +### Fixed +* Issues related to history object handling. +* Lst<>.clear() will not issue sync operation if list is empty. +* Fixed compilation issues using GCC 8. + +----------- + +### Internals +* ConstObj::to_string() added. Mostly to be used during debugging. +* SortDescriptor::is_ascending() added. Potentially to be used by OS. + +---------------------------------------------- + +# 6.0.0-alpha.12 Release notes + +### Enhancements +* Small improvements in pk handling to support ObjectStore + +---------------------------------------------- + +# 6.0.0-alpha.11 Release notes + +### Enhancements +* to_json operations reintroduced. +* get_any() and is_null() operations added to ConstLstBase +* pk handling enhanched so that it can be used by OS + +### Fixed +* None. + +---------------------------------------------- + +# 6.0.0-alpha.10 Release notes + +### Fixed +* Fixed replication of setting and inserting null values in lists. Now also fixed + for String and Binary. + +---------------------------------------------- + +# 6.0.0-alpha.9 Release notes + +### Enhancements +* Ability to get a general pointer to list object added to ConstObj + +### Fixed +* Fixed replication of null timestamps in list + +---------------------------------------------- + +# 6.0.0-alpha.8 Release notes + +### Enhancements +* All ObjectID handling functionality has been moved from Sync to Core. +* The primary key column concept is now handled in Core in a way consistent with Sync + and ObjectStore. This means that the corresponding code can be removed in those + subsystems. +* Creating tables and objects can now be done entirely using the Core interface. All + sync::create_table...() and sync::create_object...() functions now map directly to a + core function. + +---------------------------------------------- + +# 6.0.0-alpha.7 Release notes + +### Enhancements +* Ability to get/set Mixed on ConstObj/Obj. +* Now able to migrate files with Sync histories. + +----------- + +### Internals +* Class ObjectId is moved over from Sync. + +---------------------------------------------- + +# 6.0.0-alpha.6 Release notes + +----------- + +### Internals +* Column keys cannot be used across tables. If a key is obtained from one table, it can only be + used in relation to that table. + +---------------------------------------------- + +# 6.0.0-alpha.5 Release notes + +----------- + +### Internals +* realm-browser now able to read Core-6 files and print values for even more columns + +---------------------------------------------- + +# 6.0.0-alpha.4 Release notes + +### Fixed +* A lot of small fixes in order to make sync test pass. + +----------- + +### Internals +* The release binaries for Apple platforms are now built with Xcode 9.2 (up from 8.3.3). +* A new function - History::ensure_updated can be called in places where the history object + needs to be up-to-date. The function will use a flag to ensure that the object is only + updated once per transaction. +* All read transactions will have a separate history object. Read transactions + use the object stored in the Replication object. + +# 6.0.0-alpha.3 Release notes + +---------------------------------------------- + +# 6.0.0-alpha.2 Release notes + +### Open issues + +* Encrypted files may not work on Android + [Issue#248](https://github.com/realm/realm-core-private/issues/248) +* Building queries are not thread safe + [Issue#237](https://github.com/realm/realm-core-private/issues/237) + +### Bugfixes + +* A few fixes improving the stability. + +----------- + +### Internals + +* Object keys are now allowed to use all 64 bits. Only illegal value is 0xFFFFFFFFFFFFFFFF + which is the encoding of a null-value. + +---------------------------------------------- + +# 6.0.0-alpha.1 Release notes + +### Open issues + +* Encrypted files may not work on Android + [Issue#248](https://github.com/realm/realm-core-private/issues/248) +* Building queries are not thread safe + [Issue#237](https://github.com/realm/realm-core-private/issues/237) + +### Bugfixes + +* Many small fixes. + +### Breaking changes + +* DB objects are now heap allocated and accessed through a DBRef. You must create a DB using + static DB::create() function. +* All list like classes have been renamed + +### Enhancements + +* Providing option to supply initial values when creating an object. This can be used as an + optimization when some columns have a search index. Then you don't have to first insert + the default value in the index and subsequently the real value. +* Many small enhancements required by ObjectStore and Sync. + +---------------------------------------------- + +# 6.0.0-alpha.0 Release notes + +### Breaking changes + +* SharedGroup class removed. New DB class added allowing you to specify the Realm file + only once regardless of the number of threads. +* New Transaction class added, inheriting from Group. Transactions are produced by DB. +* TransactionRef added to provide reference to a Transaction. This is a std::shared_ptr. +* Old export_for_handover/import_from_handover has been removed. Handover is now direct + from one transaction to another, using a new method Transaction::import_copy_of(). +* 'begin' argument removed from Query::count(); +* Table::optimize replaced with explicit Table::enumerate_string_column. It will enumerate + column unconditionally. +* TableView::num_attached_rows() no longer available. get(), front, back()... and similar + functions will throw `InvalidKey` if the referenced object has been deleted. +* Removed the ability to sort a linklist according to some property of the target table. This + operation is not supported by the sync protocol and allegedly not used in the bindings. +* Moved `get_uncommitted_changes()` from `History` class to `Replication` class. This removes the + need to call TrivialReplication::get_uncommitted_changes from a decendant of History. +* Column identifier changed from size_t to ColKey. This identifier is subsequently + used when accessing/mutating the data and when making queries. +* Table identifiers changed from size_t to TableKey. +* Row interface on Table removed. All access to data goes through the new Obj + interface. Obj objects are created with an ObjKey identifier, which subsequently + is used to get access to and optionally delete the object. +* Support for sub-tables removed. The feature was only used to implement support for + array-of-primitives. This is now implemented by a more specific List class. +* Descriptor class removed. No longer needed as we don't have sub-tables. +* Mixed column no longer supported. Will be revived at a later point in time. +* LinkView is replaced by the more general list interface +* Limiting parameters removed from Query aggregate functions (start, end, limit) +* Table::get_range_view() has been removed. +* Table::merge_rows() not supported. Not needed anymore. +* Ref-counted freestanding tables can no longer be created (by Table::create) +* OldDateTime is no longer supported +* An old database cannot be opened without being updated to the new file version. + This implies that an old database cannot be opened in read-only, as read-only + prevents updating. +* Only file format versions from 6 and onwards can be opened (realm core v2.0.0) + +### Enhancements + +* None. Or see below. + +----------- + +### Internals + +* Major simplifications and optimizations to management of memory mappings. +* Speed improvement for Sort(). + +--------------------------------------------- + +# 5.23.7 Release notes + +### Enhancements +* Reduce the encrypted page reclaimer's impact on battery life on Apple platforms. ([PR #3461](https://github.com/realm/realm-core/pull/3461)). + +### Fixed +* macOS binaries were built with the incorrect deployment target (10.14 rather than 10.9). ([Cocoa #6299](https://github.com/realm/realm-cocoa/issues/6299), since 5.23.4). +* Subtable accessors could be double-deleted if the last reference was released from a different + thread at the wrong time. This would typically manifest as "pthread_mutex_destroy() failed", but + could also result in other kinds of crashes. ([Cocoa #6333](https://github.com/realm/realm-cocoa/issues/6333)). +* Sorting float or double columns containing NaN values had inconsistent results and would sometimes + crash due to out-of-bounds memory accesses. ([Cocoa #6357](https://github.com/realm/realm-cocoa/issues/6357)). + +---------------------------------------------- + +# 5.23.6 Release notes + +### Enhancements +* Performance significantly improved when making a query on the property of a linked table, when the property is indexed. + +### Fixed +* A race between extending the file and activity in the encryption layer could lead to crash and corruption. + This race has been fixed. The bug was introduced with version 5.3.0 and may hit on Android, if encryption is + in use. It could also affect Linux on file systems where posix prealloc() is unsupported. + ([PR #3427](https://github.com/realm/realm-core/issues/3427), since 5.3.0) +* Null values were not printed correctly when using json serialisation. ([PR #3399](https://github.com/realm/realm-core/issues/3399)). +* ListOfPrimitives were not printed correctly when using json serialisation. ([#3408](https://github.com/realm/realm-core/issues/3408)). + +----------- + +### Internals +* Fixed several warnings found by newer clang compilers. ([#3393](https://github.com/realm/realm-core/issues/3393)). + +---------------------------------------------- + +# 5.23.5 Release notes + +### Enhancements +* None. + +### Fixed +* Chained OR equals queries on an unindexed string column failed to match any results if any of the strings were 64 bytes or longer. ([PR #3386](https://github.com/realm/realm-core/pull/3386), since 5.17.0). +* Fixed serialization of a query which looks for a null timestamp. This only affects query based sync. ([PR #3389](https://github.com/realm/realm-core/pull/3388), since v5.23.2). + +### Breaking changes +* None. + +----------- + +### Internals + +* VersionID comparison operators are now const qualified ([PR #3391](https://github.com/realm/realm-core/pull/3391)). +* Exception `util::File::AccessError`, and it's derivatives such as `util::File::NotFound`, will now include a stacktrace in the message returned by the `what()` method. ([PR #3394](https://github.com/realm/realm-core/pull/3394)) + + +---------------------------------------------- + +# 5.23.4 Release notes + +### Internals +* The release binaries for Apple platforms are now built with Xcode 10.0 (up from 9.4). +* Add a Catalyst/UIKit for Mac library to the Cocoa release package. + +---------------------------------------------- + +# 5.23.3 Release notes + +### Fixed +* If a signal interrupted a msync() call, Core would throw an exception. This behavior has new been changed to retry the system call instead. (Issue [#3352](https://github.com/realm/realm-core/issues/3352)) +* Fixed a bug in sum() or average() of == and != queries on integer columns sometimes returning an incorrect result. ([#3356](https://github.com/realm/realm-core/pull/3356), since the beginning). + +----------- + +### Internals +* Changed the metrics timers to more precisely report in nanoseconds, instead of seconds. ([#3359](https://github.com/realm/realm-core/issues/3359)) +* Better performance when cloud query metrics are turned on, by not acquiring a backtrace on query serialization errors (permissions queries). ([#3361](https://github.com/realm/realm-core/issues/3361)). +* Performance improved for queries comparing a constant value to a property over unary link path (eg: "someLink.Id == 42"). ([#3670](https://github.com/realm/realm-core/issues/3370)) + +---------------------------------------------- + +# 5.23.2 Release notes + +### Fixed +* Named pipes on Android are now created with 0666 permissions instead of 0600. This fixes a bug on Huawei devices which caused named pipes to change owners during app upgrades causing subsequent `ACCESS DENIED` errors. This should have not practical security implications. (Issue [#3328](https://github.com/realm/realm-core/pull/3328)) + +----------- + +### Internals +* The release binaries for Apple platforms are now built with Xcode 9.4 (up from 9.2). +* Performance of queries on Timestamp is improved + +---------------------------------------------- + +# 5.23.1 Release notes + +### Fixed +* Fixed the metrics throwing an exception when a query cannot be serialised. Now it reports the exception message as the description. + ([#3031](https://github.com/realm/realm-sync/issues/3031), since v3.2.0) +* Queries involving an indexed int column which were constrained by a LinkList with an order different from the table's order would + give incorrect results. ([#3307](https://github.com/realm/realm-core/issues/3307), since v5.19.0) +* Queries involving an indexed int column had a memory leak if run multiple times. ([#6186](https://github.com/realm/realm-cocoa/issues/6186)), since v5.19.0) + +---------------------------------------------- + +# 5.23.0 Release notes + +### Enhancements +* Add a Swift Package Manager package ([#3308](https://github.com/realm/realm-core/pull/3308)). + +### Fixed +* Constructing an `IncludeDescriptor` made unnecessary table comparisons. This resulted in poor performance for subscriptions + using the `includeLinkingObjects` functionality. ([#3311](https://github.com/realm/realm-core/issues/3311), since v5.18.0) + +### Breaking changes +* None. + +----------- + +### Internals +* None. + +---------------------------------------------- + +# 5.22.0 Release notes + +### Enhancements + +* Expose the ability to follow links while printing a TableView in JSON format. + TableView::to_json() now supports the same arguments as Table::to_json(). + ([#3301](https://github.com/realm/realm-core/pull/3301)) + +### Fixed +* None. + +### Breaking changes +* None. + +----------- + +### Internals +* Fixed an inconsistency in the use of the `REALM_METRICS` compile time option. Now core consumers are able + to use `SharedGroup::get_metrics()` regardless of whether or not metrics are compiled in. A null pointer + is returned if the feature has been disabled at compile time. + +---------------------------------------------- + +# 5.21.0 Release notes + +### Enhancements +* Added support for unicode characters in realm path and filenames for Windows. Contribution by @rajivshah3. + ([#3293](https://github.com/realm/realm-core/pull/3293)) + +### Fixed +* None. + +### Breaking changes +* None. + +----------- + +### Internals +* Introduced new feature test macros for address and thread sanitizers in + ``. +* Added Realm file path to Allocator assertions ([3283](https://github.com/realm/realm-core/issues/3283)). + +---------------------------------------------- + +# 5.20.0 Release notes + +### Enhancements +* Added the ability to convert a std::chrono::time_point to a Timestamp and + vice versa. This allows us to make calculations using std::chrono::duration. + +### Fixed +* Slab usage was reported wrong by SlabAlloc::get_total_slab_size() ([#3284](https://github.com/realm/realm-core/pull/3284) + This caused ROS to incorectly report "exabytes" of memory used for slab. +* The control of the page reclaimer did not limit the page reclaimers workload correctly. This could lead + to the reclaimer not running as much as intended. This is not believed to have been visible to end users. + This bug also caused ROS to occasionally report odd metrics for the reclaimer. + ([#3285](https://github.com/realm/realm-core/pull/3285)) +* When opening an encrypted file via SharedGroup::open(), it could wrongly fail and indicate a file corruption + although the file was ok. + ([#3267](https://github.com/realm/realm-core/issues/3267), since core v5.12.2) + +---------------------------------------------- + +# 5.19.1 Release notes + +### Fixed +* Freelist would keep growing with a decreased commit performance as a result. + ([2927](https://github.com/realm/realm-sync/issues/2927)) +* Fixed an incorrect debug mode assertion which could be triggered when generating the description of an IncludeDescriptor. + ([PR #3276](https://github.com/realm/realm-core/pull/3276) since v5.18.0). +---------------------------------------------- + +# 5.19.0 Release notes + +### Enhancements +* Improved query performance for unindexed integer columns when the query has a chain of OR conditions. + This will improve performance of "IN" queries generated by SDKs. + ([PR #2888](https://github.com/realm/realm-sync/issues/2888). +* Use search index in queries on integer columns (equality only). This will improve performance of + queries on integer primary key properties for example. ([PR #3272](https://github.com/realm/realm-core/pull/3272)). +* Number of 8 byte blocks in freelist is minimized. This will result in a shorter freelist. + +### Fixed +* Writing a snapshot to file via Group::write() could produce a file with some parts not + reachable from top array (a memory leak). ([#2911](https://github.com/realm/realm-sync/issues/2911)) +* Fixed a bug in queries on a string column with more than two "or" equality conditions when the last condition also had an + "and" clause. For example: `first == "a" || (first == "b" && second == 1)` would be incorrectly evaluated as + `(first == "a" || first == "b")`. ([#3271](https://github.com/realm/realm-core/pull/3271), since v5.17.0) + +### Breaking changes +* None. + +----------- + +### Internals +* None. + +---------------------------------------------- + +# 5.18.0 Release notes + +### Enhancements +* Adds support for a new IncludeDescriptor type which describes arbitrary link paths + on a TableView. Applying this to a TableView does not modify the results, but gives + users the ability to use the reporting method to find rows in a different table that + are connected by backlinks. This is intended for sync subscriptions. +* Enhances LinksToNode so that it can check links to multiple targets. This can be utilized + in permissions check in sync. + +### Fixed +* None. + +----------- + +### Internals +* The release binaries for Apple platforms are now built with Xcode 9.2 (up from 8.3.3). + +---------------------------------------------- + +# 5.17.0 Release notes + +### Enhancements +* Improved query performance for unindexed string columns when the query has a chain of OR conditions. + This will improve performance of "IN" queries generated by SDKs. + ([PR #3250](https://github.com/realm/realm-core/pull/3250). + +### Fixed +* Making a query that compares two integer properties could cause a segmentation fault on the server. + ([#3253](https://github.com/realm/realm-core/issues/3253)) + +----------- + +### Internals +* The protocol for updating Replication/History is changed. The Replication object will be initialized + in every transaction. A new parameter will tell if it is a write- or readtransaction. A new function - + History::ensure_updated can be called in places where the history object needs to be up-to-date. The + function will use a flag to ensure that the object is only updated once per transaction. + +---------------------------------------------- + +# 5.16.0 Release notes + +### Enhancements +* Improved performance of encryption and decryption significantly by utilizing hardware optimized encryption functions. + ([#293](https://github.com/realm/realm-core-private/issues/293)) +* Added the ability to write a Group with the history stripped. + ([#3245](https://github.com/realm/realm-core/pull/3245)) + +### Fixed +* Nothing + +----------- + +### Internals +* Size of decrypted memory and of currently reserved slab is now available outside of the + metrics system (to which they were added in 5.15.0). This allows us to get the current + values independently from transactions start or end (the metrics system is only updated + at transaction boundaries). + ([3240] https://github.com/realm/realm-core/pull/3240) +* Current target and workload set for the page reclaimer is now also available from `get_decrypted_memory_stats()` + ([3246] https://github.com/realm/realm-core/pull/3246) +* Default heuristic for reclaiming pages holding decrypted data has been changed, now + limiting amount to same as current use of the buffer cache. Previously the limit was + half of buffer cache usage. This heuristic may still not be good enough for some scenarios + and we recommend monitoring and explicitly setting a better target in cases where we reclaim + more memory than nescessary. + (also [3240] https://github.com/realm/realm-core/pull/3240) +* Now publishing a TSAN compatible linux build. +---------------------------------------------- + +# 5.15.0 Release notes + +### Enhancements +* Metrics history is now capped to a configurable buffer size with a default of 10000 entries. + If this is exceeded without being consumed, only the most recent entries are stored. This + prevents excessive memory growth if users turn on metrics but don't use it. +* Metrics transaction objects now store the number of decrypted pages currently in memory. +* SharedGroup::get_stats includes an optional parameter to get size of currently locked memory. +* Metrics now exposes the table name of queries which have been run. + +### Fixed +* When shutting down the server you could sometimes experience a crash with "realm::util::Mutex::lock_failed" + in the stacktrace. + ([#3237](https://github.com/realm/realm-core/pull/3237), since v5.12.5) + +### Internal +* Fix a race between the encryption page reclaim governor running and setting a governor. + This only affects applications which actually set the governor to something custom which no one does yet. + ([#3239](https://github.com/realm/realm-core/issues/3239), since v5.12.2) + +---------------------------------------------- + +# 5.14.0 Release notes + +### Enhancements +* Add assertion to prevent translating a ref value that is not 8 byte aligned. This will allow + us to detect file corruptions at an earlier stage. +* You can now get size of the commit being built and the size of currently allocated slab area. +* The amount of memory held by SharedGroup is minimized as most of it will be freed after each commit. + +### Fixed +* Compacting a realm into an encrypted file could take a really long time. The process is now optimized by adjusting the write + buffer size relative to the used space in the realm. + ([#2754](https://github.com/realm/realm-sync/issues/2754)) +* Creating an object after creating an object with the int primary key of "null" would hit an assertion failure. + ([#3227](https://github.com/realm/realm-core/pull/3227)). + +### Breaking changes +* None. + +----------- + +### Internals +* The buffer size used by util::File::Streambuf is now configurable in construction. + +---------------------------------------------- + +# 5.13.0 Release notes + +### Enhancements +* The parser now supports readable timestamps with a 'T' separator in addition to the originally supported "@" separator. + For example: "startDate > 1981-11-01T23:59:59:1". ([#3198](https://github.com/realm/realm-core/issues/3198)). + +### Fixed +* If, in debug mode, you try to compute the used space on a newly compacted realm (with empty free list), the program will + abort. ([#1171](https://github.com/realm/realm-sync/issues/2724), since v5.12.0) + +### Breaking changes +* None. + +----------- + +### Internals +* For convenience, `parser::parse` now accepts a `StringData` type instead of just `std::string`. +* Parsing a query which uses the 'between' operator now gives a better error message indicating + that support is not yet implemented. ([#3198](https://github.com/realm/realm-core/issues/3198)). + +---------------------------------------------- + +# 5.12.7 Release notes + +### Enhancements +* Instead of asserting, an `InvalidDatabase` exception is thrown when a realm file is opened + with an invalid top ref. Name of problematic file is included in exception message. + +### Fixed +* A bug was fixed in `realm::util::DirScanner` that could cause it to sometimes + skip directory entries due to faulty error handling around `readdir()`. + (Issue [realm-sync#2699](https://github.com/realm/realm-sync/issues/2699), since 5.12.5). + +### Breaking changes +* None. + +----------- + +### Internals +* Improved performance on `find_first` for small string arrays (ArrayString). This will improve the table name lookup + performance. +* Upgrade pegtl to 2.6.1. Several issues fixed. +* Introduced Durability::Unsafe, which disables sync'ing to disk. Using this option, + a platform crash may corrupt the realm file. Use only, if you'r OK with this. + +---------------------------------------------- + +# 5.12.6 Release notes + +### Enhancements +* None. + +### Fixed +* On AWS Lambda we may throw an "Operation not permitted" exception when calling posix_fallocate(). + A slower workaround has been supplied. + ([#3193](https://github.com/realm/realm-core/issues/3293)) + +### Breaking changes +* None. + +----------- + +### Internals +* None. + +---------------------------------------------- + +# 5.12.5 Release notes + +### Enhancements +* None. + +### Fixed +* When loading the realm binary from within the realm-js SDK, core could hang on Windows as described in + https://github.com/realm/realm-js/issues/2169. + ([#3188](https://github.com/realm/realm-core/pull/3188, since 5.12.2) + +### Breaking changes +* None. + +----------- + +### Internals +* Fixed warnings reported by GCC 8. +* Replaced call to the deprecated `readdir_r()` with `readdir()`. +* Compilation without encryption now possible + +---------------------------------------------- + +# 5.12.4 Release notes + +### Enhancements +* None. + +### Fixed +* A segmentation fault would occur when calling Group:get_used_space() for a realm file + with no commits. This method would usually only be called from sync/ROS to calculate + and report state size. + ([#3182](https://github.com/realm/realm-core/issues/3182), since v5.12.0) + +### Breaking changes +* None. + +---------------------------------------------- + +# 5.12.3 Release notes + +### Enhancements +* None. + +### Fixed +* Added assertions around use of invalid refs and sizes. Helps in narrowing down the causes for + asserts like `ref != 0` and `(chunk_pos % 8) == 0` + +### Breaking changes +* None. + +----------- + +### Internals +* None. + +---------------------------------------------- + +# 5.12.2 Release notes + +### Enhancements +* None + +### Fixed +* If encryption was enabled, decrypted pages were not released until the file was closed, causing + excessive usage of memory. + A page reclaim daemon thread has been added, which will work to release decrypted pages back to + the operating system. To control it, a governing function can be installed. The governing function + sets the target for the page reclaimer. If no governing function is installed, the system will attempt + to keep the memory usage below any of the following: + + - 1/4 of physical memory available on the platform as reported by "/proc/meminfo" + - 1/4 of allowed memory available as indicated by "/sys/fs/cgroup/memory/memory_limit_in_bytes" + - 1/2 of what is used by the buffer cache as indicated by "/sys/fs/cgroup/memory/memory.stat" + - A target directly specified as "target " in a configuration file specified + by the environment variable REALM_PAGE_GOVERNOR_CFG. + if none of the above is available, or if a target of -1 is given, the feature is disabled. + ([#3123](https://github.com/realm/realm-core/issues/3123)) + +----------- + +### Internals +* None. + +---------------------------------------------- + +# 5.12.1 Release notes + +### Enhancements +* Illegal freeing of in-file-memory is now detected when freeing is + actually done. This will make it easier to find the root cause of + some file corruption issues. + +### Fixed +* None. + +### Breaking changes +* None. + +----------- + +### Internals +* None. + +---------------------------------------------- + +# 5.12.0 Release notes + +### Enhancements +* Added Group::get_used_space() which will return the size of the data taken up by the current + commit. This is in contrast to the number returned by SharedGroup::get_stats() which will + return the size of the last commit done in that SharedGroup. If the commits are the same, + the number will of course be the same. + Issue [#259](https://github.com/realm/realm-core-private/issues/259) + +### Fixed +* None. + +### Breaking changes +* The way the Linux binaries are delivered is changed. They are now distributed + like the rest of the binaries with two packages (devel/runtime) per build type. + The file names follow this scheme: + realm-core---Linux-{devel|runtime}.tar.gz + For Linux the following build types are published: Debug, Release, RelAssert + and RelASAN. + +----------- + +### Internals +* Replication::get_database_path() is made const. +* TrivialReplication::get_database_path() is made public. +* Added better compatibility for custom allocators with standard library + containers on GCC 4.9. + +---------------------------------------------- + +# 5.11.3 Release notes + +### Compatibility +* File format: ver. 9 + Upgrades automatically from previous formats. + Can open realms down to file format version 7 in ReadOnly mode (without upgrade). + +----------- + +### Internals +* Improved assertion checking in release mode in order to detect any corruption + of our freelist earlier and prevent bogus allocations from a corrupted freelist + from leading to subsequent corruption of other parts of the file. + +---------------------------------------------- + +# 5.11.2 Release notes + +### Compatibility +* File format: ver. 9 + Upgrades automatically from previous formats. + Can open realms down to file format version 7 in ReadOnly mode (without upgrade). + +----------- + +### Internals +* Releases no longer include RPM and DEB packages. +* Releases now include RelWithDebInfo+ASAN and RelWithDebInfo+Assertions tarballs for linux. + [#3112](https://github.com/realm/realm-core/pull/3112). + +---------------------------------------------- + +# 5.11.1 Release notes + +### Compatibility +* File format: ver. 9 + Upgrades automatically from previous formats. + Can open realms down to file format version 7 in ReadOnly mode (without upgrade). + +----------- + +### Internals +* Fixed a bug in the use of placement new on MSVC, where the implementation is + buggy. This bug only affected version 5.11.0. + PR [#3109](https://github.com/realm/realm-core/pull/3109) +* Made improvements to the custom allocation interfaces introduced in 5.11.0, + which should make them more convenient and use slightly less memory. + PR [#3108](https://github.com/realm/realm-core/pull/3108) + +---------------------------------------------- + +# 5.11.0 Release notes + +### Compatibility +* File format: ver. 9 + Upgrades automatically from previous formats. + Can open realms down to file format version 7 in ReadOnly mode (without upgrade). + +----------- + +### Internals +* Added support for custom heap allocators + PR [#3106](https://github.com/realm/realm-core/pull/3106). + +---------------------------------------------- + +# 5.10.3 Release notes + +### Fixed +* When a sort or distinct over links was done on an already-sorted TableView, + the link translation map was done using the unsorted rows, resulting in the + second sort/distinct being done with the incorrect values. + PR [#3102](https://github.com/realm/realm-core/pull/3102). + +### Compatibility +* File format: ver. 9 (upgrades automatically from previous formats) + +----------- + +### Internals + +* Will assert if someone tries to free a null ref. + Issue [#254](https://github.com/realm/realm-core-private/issues/254) and the like. + +---------------------------------------------- + +# 5.10.2 Release notes + +### Enhancements + +* Add an arm64_32 slice to the watchOS build. + +---------------------------------------------- + +# 5.10.1 Release notes + +### Internals + +* Stack trace also available when throwing std:: exceptions. + +---------------------------------------------- + +# 5.10.0 Release notes + +### Enhancements + +* Allow compact to take an optional output encryption key. + PR [#3090](https://github.com/realm/realm-core/pull/3090). + +---------------------------------------------- + +# 5.9.0 Release notes + +### Enhancements + +* Allow a version number in Group::write which will cause a file with (sync) + history to be written. + +----------- + +### Internals + +* Most exception types now report the stack trace of the point where they were + thrown in their `what()` message. This is intended to aid debugging. + Additionally, assertion failures on Linux now report their stack traces as + well, similar to Apple platforms. Recording stack traces is only supported on + Linux (non-Android) and Apple platforms for now. + +---------------------------------------------- + +# 5.8.0 Release notes + +### Bugfixes + +* Fix a crash on some platforms when using the query parser to look for a string + or binary object which has a certain combination of non-printable characters. + +### Enhancements + +* Support limiting queries via `DescriptorOrdering::set_limit` and by supporting + "LIMIT(x)" in string queries. + Issue [realm_sync:#2223](https://github.com/realm/realm-sync/issues/2223) + +---------------------------------------------- + +# 5.7.2 Release notes + +### Bugfixes + +* Fix a use-after-free when an observer is passed to rollback_and_continue_as_read(). + +### Enhancements + +* More informative InvalidDatabase exception messages + Issue [#3075](https://github.com/realm/realm-core/issues/3075). + +---------------------------------------------- + +# 5.7.1 Release notes + +### Bugfixes + +* Fix crash in Group::compute_aggregated_byte_size() when applied on an empty + realm file. (Issue #3072) + +---------------------------------------------- + +# 5.7.0 Release notes + +### Enhancements + +* Improved Group::compute_aggregated_byte_size() allowing us to differentiate + between state, history and freelists. + (Issue #3063) + +---------------------------------------------- + +# 5.6.5 Release notes + +### Enhancements + +* Improved scalability for the slab allocator. This allows for larger + transactions. (PR #3067) + +---------------------------------------------- + +# 5.6.4 Release notes + +### Enhancements + +* Add Table::add_row_with_keys(), which allows + sync::create_object_with_primary_key() to avoid updating the index twice when + creating an object with a string primary key. +* Improved the performance of setting a link to its current value. + +---------------------------------------------- + +# 5.6.3 Release notes + +### Enhancements + +* Improved scalability for in-file freelist handling. This reduces + commit overhead on large transactions. +* Improved scalability for in-file allocation during commit. +* Minimized use of memory mappings and msync() on large commits + on devices which can support large address spaces. + +---------------------------------------------- + +# 5.6.2 Release notes + +### Bugfixes + +* Fix curruption of freelist with more than 2M entries. + PR [#3059](https://github.com/realm/realm-core/pull/3059). + +---------------------------------------------- + +# 5.6.1 Release notes + +### Bugfixes + +* More readable error message in the query parser when requesting an a bad argument. +* Don't write history information in `SharedGroup::compact()` for + non-syncronized Realms. + +----------- + +### Internals + +* Restore -fvisibility-inlines-hidden for the binaries for Apple platforms. +* Remove a few warnings at compile time. +* Improve error detection related to memory allocation/release + +---------------------------------------------- + +# 5.6.0 Release notes + +### Bugfixes + +* In the parser, fix `@links.@count` when applied over lists to return + the sum of backlinks for all connected rows in the list. +* Fix null comparisons in queries not serialising properly in some cases. + Also explicitly disable list IN list comparisons since its not supported. + PR [#3037](https://github.com/realm/realm-core/pull/3037). + +### Enhancements + +* `SharedGroup::compact()` now also compacts history information, which means + that Sync'ed Realm files can now be compacted (under the usual restrictions; + see `group_shared.hpp` for details). + +---------------------------------------------- + +# 5.5.0 Release notes + +### Enhancements + +* Parser improvements: + - Allow an arbitrary prefix on backlink class names of @links queries. + This will allow users to query unnamed backlinks using the `@links.Class.property` syntax. + - Case insensitive `nil` is now recognised as a synonym to `NULL`. + - Add support for `@links.@count` which gives the count of all backlinks to an object. + See Issue [#3003](https://github.com/realm/realm-core/issues/3003). + +----------- + +### Internals + +* Apple binaries are now built with Xcode 8.3.3. + +---------------------------------------------- + +# 5.4.2 Release notes + +### Bugfixes + +* Fix sporadic failures of disk preallocation on APFS. + PR [#3028](https://github.com/realm/realm-core/pull/3028). + +---------------------------------------------- + +# 5.4.1 Release notes + +### Enhancements + +* Reduced the number of files opened when the async commit daemon is not used. + PR [#3022](https://github.com/realm/realm-core/pull/3022). + +----------- + +### Internals + +* Exported CMake targets have been renamed to "modern" conventions, e.g. + `Realm::Core` and `Realm::QueryParser`. + +---------------------------------------------- + +# 5.4.0 Release notes + +### Bugfixes + +* Fixed usage of disk space preallocation which would occasionally fail on recent MacOS + running with the APFS filesystem. PR [#3013](https://github.com/realm/realm-core/pull/3013). + Issue [#3005](https://github.com/realm/realm-core/issues/3005). +* Fixed a bug in queries containing 'or' at different nesting levels. + PR [#3006](https://github.com/realm/realm-core/pull/3006). + +### Breaking changes + +* None. + +### Enhancements + +* Added `Table::get_link_type()` as a helper method for getting the link type from link columns. + PR [#2987](https://github.com/realm/realm-core/pull/2987). + +----------- + +### Internals + +* Silenced a false positive strict aliasing warning. + PR [#3002](https://github.com/realm/realm-core/pull/3002). +* Assertions will print more information in relase mode. + PR [#2982](https://github.com/realm/realm-core/pull/2982). + +---------------------------------------------- + +# 5.3.0 Release notes + +### Bugfixes + +* Fixed handling of out-of-diskspace. With encryption in use it would ASSERT like + `group_writer.cpp:393: [realm-core-5.1.2] Assertion failed: ref + size <= ...`. + Without encryption it would give a SIGBUS error. It's unknown if it could corrupt + the .realm file. +* Fix an issue where adding zero rows would add the default value to the keys + of any string enum columns. Not affecting end users. + PR [#2956](https://github.com/realm/realm-core/pull/2956). + +### Enhancements + +* Parser improvements: + - Support subquery count expressions, for example: "SUBQUERY(list, $x, $x.price > 5 && $x.colour == 'blue').@count > 1" + - Subqueries can be nested, but all properties must start with the closest variable (no parent scope properties) + - Support queries over unnamed backlinks, for example: "@links.class_Person.items.cost > 10" + - Backlinks can be used like lists in expressions including: min, max, sum, avg, count/size, and subqueries + - Keypath substitution is supported to allow querying over named backlinks and property aliases, see `KeyPathMapping` + - Parsing backlinks can be disabled at runtime by configuring `KeyPathMapping::set_allow_backlinks` + - Support for ANY/SOME/ALL/NONE on list properties (parser only). For example: `ALL items.price > 10` + - Support for operator 'IN' on list properties (parser only). For example: `'milk' IN ingredients.name` + PR [#2989](https://github.com/realm/realm-core/pull/2989). + +----------- + +### Internals + +* Add support for libfuzzer. + PR [#2922](https://github.com/realm/realm-core/pull/2922). + +---------------------------------------------- + +# 5.2.0 Release notes + +### Bugfixes + +* Fix a crash when distinct is applied on two or more properties where + the properties contain a link and non-link column. + PR [#2979](https://github.com/realm/realm-core/pull/2979). + +### Enhancements + +* Parser improvements: + - Support for comparing two columns of the same type. For example: + - `wins > losses` + - `account_balance > purchases.@sum.price` + - `purchases.@count > max_allowed_items` + - `team_name CONTAINS[c] location.city_name` + - Support for sort and distinct clauses + - At least one query filter is required + - Columns are a comma separated value list + - Order of sorting can be `ASC, ASCENDING, DESC, DESCENDING` (case insensitive) + - `SORT(property1 ASC, property2 DESC)` + - `DISTINCT(property1, property2)` + - Any number of sort/distinct expressions can be indicated + - Better support for NULL synonym in binary and string expressions: + - `name == NULL` finds null strings + - `data == NULL` finds null binary data + - Binary properties can now be queried over links + - Binary properties now support the full range of string operators + (BEGINSWITH, ENDSWITH, CONTAINS, LIKE) + PR [#2979](https://github.com/realm/realm-core/pull/2979). + +----------- + +### Internals + +* The devel-dbg Linux packages now correctly include static libraries instead of shared ones. + +---------------------------------------------- + +# 5.1.2 Release notes + +### Bugfixes + +* Include the parser libs in the published android packages. + +---------------------------------------------- + +# 5.1.1 Release notes + +### Bugfixes + +* The `realm-parser` static library now correctly includes both simulator and device architectures on Apple platforms. + +---------------------------------------------- + +# 5.1.0 Release notes + +### Enhancements + +* Change the allocation scheme to (hopefully) perform better in scenarios + with high fragmentation. + PR [#2963](https://github.com/realm/realm-core/pull/2963) +* Avoid excessive bumping of counters in the version management machinery that is + responsible for supporting live queries. We now prune version bumping earlier if + when we have sequences of changes without queries in between. + PR [#2962](https://github.com/realm/realm-core/pull/2962) + +---------------------------------------------- + +# 5.0.1 Release notes + +### Bugfixes + +* Add a CMake import target for the `realm-parser` library. + +---------------------------------------------- + +# 5.0.0 Release notes + +### Bugfixes + +* Fix possible corruption or crashes when a `move_row` operates on a subtable. + PR [#2927](https://github.com/realm/realm-core/pull/2926). +* Table::set_int() did not check if the target column was indeed type_Int. It + will now assert like the other set methods. + +### Breaking changes + +* Remove support for the (unused) instructions for moving columns and moving tables. + This is not a file format breaking change as the instructions are still recognised, + but now a parser error is thrown if either one is seen in the transaction logs. + PR [#2926](https://github.com/realm/realm-core/pull/2926). + +### Enhancements + +* Attempted to fix a false encryption security warning from IBM Bluemix. PR [#2911] +* Utilities gain `Any` from object store and base64 encoding from sync. +* Initial support for query serialisation. +* The query parser from the object store was moved to core. + It also gained the following enhancements: + - Support @min, @max, @sum, @avg for types: int, double, float + - Support @count, @size interchangeably for types list, string, binary + - Support operator "LIKE" on strings + - Support operators: =>, =<, <>, which are synonymns for >=, <=, and != respectively + - Boolean types can now check against “0” or “1” in addition to false and true + - Fixed "not" and "or" not being applied to TRUEPREDICATE or FALSEPREDICATE + - Add support for comparing binary and string types using a (internal) base64 format: B64”…” + - Add support for Timestamps + - Internal format “Tseconds:nanoseconds” + - Readable format “YYYY-MM-DD@HH:MM:SS:NANOSECONDS” + - The nanoseconds part can be optionally omitted + - Conversion works for UTC from dates between ~1970-3000 on windows and ~1901-2038 on other platforms + PR [#2947](https://github.com/realm/realm-core/pull/2947). + +---------------------------------------------- + +# 4.0.4 Release notes + +### Bugfixes + +* Publish the release version of Android-armeabi-v7a binary. + +---------------------------------------------- + +# 4.0.3 Release notes + +### Bugfixes + +* Switch from using a combination of file write and mmap to using only mmap when + initializing the lockfile. It is unclear if this counts as a bugfix, because + it is unclear if there are still systems out there with problems handling that + scenario. The hope is that it will fix some non-reproducible problems related to + lockfile initialization. + PR [#2902](https://github.com/realm/realm-core/pull/2902) +* Make calls to posix_fallocate() robust against interruption and report + the correct error on failure. + PR [#2905](https://github.com/realm/realm-core/pull/2905). +* Fix an error in `int_multiply_with_overflow_detect()` which would report + overflow when no overflow should occur. This could cause out of memory + exceptions when the `TransactLogParser` reads strings or binary data > 2GB. + PR [#2906](https://github.com/realm/realm-core/pull/2906). + +---------------------------------------------- + +# 4.0.2 Release notes + +### Bugfixes + +* Fix a race between SharedGroup::compact() and SharedGroup::open(). The race could + cause asserts indicating file corruption even if no corruption is caused. It is also + possible that it could cause real file corruption, though that is much less likely. + PR [#2892](https://github.com/realm/realm-core/pull/2892) + +---------------------------------------------- + +# 4.0.1 Release notes + +### Bugfixes + +* Fix case insensitive contains query for null strings not returning all results and + Fix case insensitive equals query for null strings returning nothing when null strings exist. + PR [#2871](https://github.com/realm/realm-core/pull/2871). +* Added mentioning of bugfix #2853 to this file for Core 4.0.0. (see 4.0.0 below) + The mentioning of this fix for 4.0 was originally ommitted. + +---------------------------------------------- + +# 4.0.0 Release notes + +### Bugfixes + +* Fix a bug in subtable management which caused crashes if a subtable was destroyed + on a different thread. + PR [#2855](https://github.com/realm/realm-core/pull/2855). +* Fix corruption caused by `swap_rows()` and `move_column()` operations applied + to a StringEnumColumn. Currently unused by bindings. + PR [#2780](https://github.com/realm/realm-core/pull/2780). + +### Breaking changes + +* Add `Table::move_row()`. + PR [#2873](https://github.com/realm/realm-core/pull/2873). +* Changing instruction values for `Table::move_row()` requires a version bump to 9. + Version 8 files in read only mode without any history can be opened without upgrading. + PR [#2877](https://github.com/realm/realm-core/pull/2877). + +### Enhancements + +* Add method to recursively delete an object tree + PR [#2752](https://github.com/realm/realm-core/pull/2752) + Issue [#2718](https://github.com/realm/realm-core/issues/2718) +* Add method to safely delete or otherwise manipulate realm file + and management files. + PR [#2864](https://github.com/realm/realm-core/pull/2864) + +----------- + +### Internals + +* A specialised exception realm::OutOfDiskSpace is thrown instead of a generic + runtime exception when writing fails because the disk is full or the user exceeds + the allotted disk quota. + PR [#2861](https://github.com/realm/realm-core/pull/2861). + +---------------------------------------------- + +# 3.2.1 Release notes + +### Bugfixes + +* Compact now throws an exception if writing fails for some reason + instead of ignoring errors and possibly causing corruption. + In particular, this solves file truncation causing "bad header" exceptions + after a compact operation on a file system that is running out of disk space. + PR [#2852](https://github.com/realm/realm-core/pull/2852). + +----------- + +### Internals + +* Moved object store's true and false query expressions down to core. + PR [#2857](https://github.com/realm/realm-core/pull/2857). + +---------------------------------------------- + +# 3.2.0 Release notes + +### Enhancements + +* Added metrics tracking as an optional SharedGroup feature. + PR [#2840](https://github.com/realm/realm-core/pull/2840). + +----------- + +### Internals + +* Improve crash durability on windows. + PR [#2845](https://github.com/realm/realm-core/pull/2845). +* Removed incorrect string column type traits, which could cause errors. + They were unused. PR [#2846](https://github.com/realm/realm-core/pull/2846). + +---------------------------------------------- + +# 3.1.0 Release notes + +### Bugfixes + +* A linker error in some configurations was addressed by adding an explicit + instantiation of `Table::find_first` for `BinaryData`. + [#2823](https://github.com/realm/realm-core/pull/2823) + +### Enhancements + +* Implemented `realm::util::File::is_dir`, `realm::util::File::resolve`, + and `realm::util::DirScanner` on Windows. + +---------------------------------------------- + +# 3.0.0 Release notes + +### Bugfixes + +* Fixed handle leak on Windows (https://github.com/realm/realm-core/pull/2781) +* Fixed a use-after-free when a TableRef for a table containing a subtable + outlives the owning group. + +### Breaking changes + +* Added support for compound sort and distinct queries. + - Multiple consecutive calls to sort or distinct compound on each other + in the order applied rather than replacing the previous one. + - The order that sort and distinct are applied can change the query result. + - Applying an empty sort or distinct descriptor is now a no-op, this + could previously be used to clear a sort or distinct operation. + PR [#2644](https://github.com/realm/realm-core/pull/2644) +* Support for size query on LinkedList removed. This is perhaps not so + breaking after all since it is probably not used. + PR [#2532](https://github.com/realm/realm-core/pull/2532). +* Replication interface changed. The search index functions now operate + on a descriptor and not a table. + PR [#2561](https://github.com/realm/realm-core/pull/2561). +* New replication instruction: instr_AddRowWithKey +* Add the old table size to the instr_TableClear replication instruction. +* Throw a MaximumFileSizeExceeded exception during commits or allocations + instead of causing corruption or asserting. This would most likely be + seen when creating large Realm files on 32 bit OS. + PR [#2795](https://github.com/realm/realm-core/pull/2795). + +### Enhancements + +* Enhanced support for query in subtables: + Query q = table->column(0).list() == 5; + Query q = table->column(0).list().min() >= 2; + Query q = table->column(1).list().begins_with("Bar"); + PR [#2532](https://github.com/realm/realm-core/pull/2532). +* Subtable column can now be nullable. You can use `is_null()` and `set_null()` + on a subtable element. + PR [#2560](https://github.com/realm/realm-core/pull/2560). +* Support for search index on subtable columns. Only one level of subtables + are currently supported, that is, you cannot create a search index in a + subtable of a subtable (will throw exception). NOTE: Core versions prior to + this version will not be able to open .realm files of this Core version if + this Core version has added such indexes. Adding or removing an index will + take place for *all* subtables in a subtable column. There is no way to add + or remove it from single individual subtables. + PR [#2561](https://github.com/realm/realm-core/pull/2561). +* Support for encryption on Windows (Win32 + UWP). + PR [#2643](https://github.com/realm/realm-core/pull/2643). +* Add Table::add_row_with_key(). Adds a row and fills an integer column with + a value in one operation. + PR [#2596](https://github.com/realm/realm-core/pull/2596) + Issue [#2585](https://github.com/realm/realm-core/issues/2585) +* Add more overloads with realm::null - PR [#2669](https://github.com/realm/realm-core/pull/2669) + - `size_t Table::find_first(size_t col_ndx, null)` + - `OutputStream& operator<<(OutputStream& os, const null&)` + +----------- + +### Internals + +* The RuntimeLibrary of the Windows build is changed from MultiThreadedDLL to + just MultiThreaded so as to statically link the Visual C++ runtime libraries, + removing the onus on end-users to have the correct runtime redistributable + package or satellite assembly pack installed. Libraries that link against Core + on Windows will have to adjust their compiler flags accordingly. + PR [#2611](https://github.com/realm/realm-core/pull/2611). +* Win32+UWP: Switched from pthread-win32 to native API. + PR [#2602](https://github.com/realm/realm-core/pull/2602). +* Implemented inter-process CondVars on Windows (Win32 + UWP). They should be + fair and robust. + PR [#2497](https://github.com/realm/realm-core/pull/2497). +* The archives produced by the packaging process for Mac builds are now + .tar.gz files rather than .tar.xz files, with the exception of the aggregate + realm-core-cocoa-VERSION.tar.xz archive, which remains as a .tar.xz file. + +---------------------------------------------- + +# 2.9.2 Release notes + +### Bugfixes + +* Throw a MaximumFileSizeExceeded exception during commits or allocations + instead of causing corruption or asserting. This would most likely be + seen when creating large Realm files on 32 bit OS. + PR [#2795](https://github.com/realm/realm-core/pull/2795). + +**Note: This is a hotfix release built on top of 2.9.1. The above fixes are not present in version 3.0.0.** + +---------------------------------------------- + +# 2.9.1 Release notes + +### Bugfixes + +* A linker error in some configurations was addressed by adding an explicit + instantiation of `Table::find_first` for `BinaryData`. + [#2823](https://github.com/realm/realm-core/pull/2823). + +----------- + +### Internals + +* The archives produced by the packaging process for Mac builds are now + .tar.gz files rather than .tar.xz files, with the exception of the aggregate + realm-core-cocoa-VERSION.tar.xz archive, which remains as a .tar.xz file. + +**Note: This is a hotfix release built on top of 2.9.0. The above fixes are not present in version 3.0.0.** + +---------------------------------------------- + +# 2.9.0 Release notes + +### Bugfixes + +* Attempting to open a small unencrypted Realm file with an encryption key would + produce an empty encrypted Realm file. Fixed by detecting the case and + throwing an exception. + PR [#2645](https://github.com/realm/realm-core/pull/2645) +* Querying SharedGroup::wait_for_change() immediately after a commit() + would return instead of waiting for the next change. + PR [#2563](https://github.com/realm/realm-core/pull/2563). +* Opening a second SharedGroup may trigger a file format upgrade if the history + schema version is non-zero. + Fixes issue [#2724](https://github.com/realm/realm-core/issues/2724). + PR [#2726](https://github.com/realm/realm-core/pull/2726). +* Fix incorrect results from TableView::find_first(). +* Fix crash on rollback of Table::optimize(). Currently unused by bindings. + PR [#2753](https://github.com/realm/realm-core/pull/2753). +* Update frozen TableViews when Table::swap() is called. + PR [#2757](https://github.com/realm/realm-core/pull/2757). + +### Enhancements + +* Add method to get total count of backlinks for a row. + PR [#2672](https://github.com/realm/realm-core/pull/2672). +* Add try_remove_dir() and try_remove_dir_recursive() functions. + +----------- + +### Internals + +* On Apple platforms, use `os_log` instead of `asl_log` when possible. + PR [#2722](https://github.com/realm/realm-core/pull/2722). + +---------------------------------------------- + +# 2.8.6 Release notes + +### Bugfixes +* Fixed a bug where case insensitive queries wouldn't return all results. + PR [#2675](https://github.com/realm/realm-core/pull/2675). + +---------------------------------------------- + +# 2.8.5 Release notes + +### Internals + +* `_impl::GroupFriend::get_top_ref()` was added. + PR [#2683](https://github.com/realm/realm-core/pull/2683). + +---------------------------------------------- + +# 2.8.4 Release notes + +### Bugfixes + +* Fixes bug in encryption that could cause deadlocks/hangs and possibly + other bugs too. + Fixes issue [#2650](https://github.com/realm/realm-core/pull/2650). + PR [#2668](https://github.com/realm/realm-core/pull/2668). + +----------- + +### Internals + +* Fix an assert that prevented `Group::commit()` from discarding history from a + Realm file opened in nonshared mode (via `Group::open()`, as opposed to + `SharedGroup::open()`). + PR [#2655](https://github.com/realm/realm-core/pull/2655). +* Improve ASAN and TSAN build modes (`sh build.sh asan` and `sh build.sh tsan`) + such that they do not clobber the files produced during regular builds, and + also do not clobber each others files. Also `UNITTEST_THREADS` and + `UNITTEST_PROGRESS` options are no longer hard-coded in ASAN and TSAN build + modes. + PR [#2660](https://github.com/realm/realm-core/pull/2660). + +---------------------------------------------- + +# 2.8.3 Release notes + +### Internals + +* Disabled a sleep in debug mode that was impairing external tests. + PR [#2651](https://github.com/realm/realm-core/pull/2651). + +---------------------------------------------- + +# 2.8.2 Release notes + +### Bugfixes + +* Now rejecting a Realm file specifying a history schema version that is newer + than the one expected by the code. + PR [#2642](https://github.com/realm/realm-core/pull/2642). +* No longer triggering a history schema upgrade when opening an empty Realm file + (when `top_ref` is zero). + PR [#2642](https://github.com/realm/realm-core/pull/2642). + +---------------------------------------------- + +# 2.8.1 Release notes + +### Bugfixes + +* Add #include in alloc.hpp. + PR [#2622](https://github.com/realm/realm-core/pull/2622). +* Fix crash in large (>4GB) encrypted Realm files. + PR [#2572](https://github.com/realm/realm-core/pull/2572). +* Fix missing symbols for some overloads of Table::find_first + in some configurations. + PR [#2624](https://github.com/realm/realm-core/pull/2624). + +---------------------------------------------- + +# 2.8.0 Release notes + +### Bugfixes + +* Fix a race condition in encrypted files which can lead to + crashes on devices using OpenSSL (Android). + PR [#2616](https://github.com/realm/realm-core/pull/2616). + +### Enhancements + +* Enable encryption on watchOS. + Cocoa issue [#2876](https://github.com/realm/realm-cocoa/issues/2876). + PR [#2598](https://github.com/realm/realm-core/pull/2598). +* Enforce consistent use of encryption keys across all threads. + PR [#2558](https://github.com/realm/realm-core/pull/2558). + +---------------------------------------------- + +# 2.7.0 Release notes + +### Bugfixes + +* Fix for creating process-shared mutex objects in the wrong kernel object namespace on UWP. + PR [#2579](https://github.com/realm/realm-core/pull/2579). + +### Enhancements + +* Add `Group::compute_aggregated_byte_size()` and + `Table::compute_aggregated_byte_size()` for debugging/diagnostics purposes. + PR [#2591](https://github.com/realm/realm-core/pull/2591). +* `Table` and `TableView` refactoring and improvements. + PR [#2571](https://github.com/realm/realm-core/pull/2571). + * Add a templated version of `Table::set()` to go with `Table::get()`. + * Add `TableView::find_first_timestamp()`. + * Add `TableView::find_first()`. + * Make `Table::find_first()` public and add support for most column types. + * Add wrappers for `Table::set()` to `Row`. + * Add support for all column types in `Table::get()`. + +----------- + +### Internals + +* Make `Array::stats()` available in release mode builds (not just in debug mode + builds). + PR [#2591](https://github.com/realm/realm-core/pull/2591). + +---------------------------------------------- + +# 2.6.2 Release notes + +### Bugfixes + +* Fix for incorrect, redundant string index tree traversal for case insensitive searches + for strings with some characters being identical in upper and lower case (e.g. numbers). + PR [#2578](https://github.com/realm/realm-core/pull/2578), + Cocoa issue [#4895](https://github.com/realm/realm-cocoa/issues/4895) + +---------------------------------------------- + +# 2.6.1 Release notes + +### Bugfixes + +* `mkfifo` on external storage fails with `EINVAL` on some devices with Android 7.x, + which caused crash when opening Realm. + PR[#2574](https://github.com/realm/realm-core/pull/2574), + Issue [#4461](https://github.com/realm/realm-java/issues/4461). + +---------------------------------------------- + +# 2.6.0 Release notes + +### Bugfixes + +* Work around a bug in macOS which could cause a deadlock when trying to obtain a shared lock + using flock(). PR [#2552](https://github.com/realm/realm-core/pull/2552), + issue [#2434](https://github.com/realm/realm-core/issues/2434). + +### Enhancements + +* Add support for `SharedGroup::try_begin_write()` and corresponding `try_lock()` + functionality in low level Mutex classes. + PR [#2547](https://github.com/realm/realm-core/pull/2547/files) + Fixes issue [#2538](https://github.com/realm/realm-core/issues/2538) +* New file system utility functions: `util::remove_dir_recursive()` and + `util::File::for_each()`. PR [#2556](https://github.com/realm/realm-core/pull/2556). +* Made case insensitive queries use the new index based case insensitive search. + PR [#2486](https://github.com/realm/realm-core/pull/2486) + +---------------------------------------------- + +# 2.5.1 Release notes + +### Enhancements + +* Restore support for opening version 6 files in read-only mode. + PR [#2549](https://github.com/realm/realm-core/pull/2549). + +---------------------------------------------- + +# 2.5.0 Release notes + +### Bugfixes + +* Fixed a crash when rolling back a transaction which set binary or string data + inside a Mixed type. + PR [#2501](https://github.com/realm/realm-core/pull/2501). +* Properly refresh table accessors connected by backlinks to a row that has had + a `merge_rows` instruction applied and then rolled back. This could have + caused corruption if this scenario was triggered but since sync does not use + the `merge_rows` instruction in this way, this is a preventative fix. + PR [#2503](https://github.com/realm/realm-core/pull/2503). +* Fixed an assertion on a corner case of reallocation on large arrays. + PR [#2500](https://github.com/realm/realm-core/pull/2500). + Fixes issue [#2451](https://github.com/realm/realm-core/issues/2451). + +### Breaking changes + +* Disable copying of various classes to prevent incorrect use at compile time. + PR [#2468](https://github.com/realm/realm-core/pull/2468). +* History type enumeration value `Replication::hist_Sync` renamed to + `Replication::hist_SyncClient`. + PR [#2482](https://github.com/realm/realm-core/pull/2482). +* Bumps file format version from 6 to 7 due to addition of a 10th element into + `Group::m_top`. The new element is the history schema version, which is + crucial for managing the schema upgrade process of sync-type histories in a + way that is independent of core's Realm file format. The bump is necessary due + to lack of forwards compatibility. The changes are backwards compatible, and + automatic upgrade is implemented. + PR [#2481](https://github.com/realm/realm-core/pull/2481). +* New pure virtual methods `get_history_schema_version()`, + `is_upgradable_history_schema()`, and `upgrade_history_schema()` in + `Replication` interface. + PR [#2481](https://github.com/realm/realm-core/pull/2481). + +### Enhancements + +* Support setting Mixed(Timestamp) through the transaction logs. + PR [#2507](https://github.com/realm/realm-core/pull/2507). +* Implement comparison of Mixed objects containing Timestamp types. + PR [#2507](https://github.com/realm/realm-core/pull/2507). +* Allow query for size of strings, binaries, linklists and subtables: + Query q = table->where().size_equal(2, 5); + Query q = table1->column(2).size() == 5; + PR [#2504](https://github.com/realm/realm-core/pull/2504). +* New history type enumeration value `Replication::hist_SyncServer`. This allows + for the sync server to start using the same kind of in-Realm history scheme as + is currently used by clients. + PR [#2482](https://github.com/realm/realm-core/pull/2482). + +----------- + +### Internals + +* `StringIndex` now supports case insensitive searches. + PR [#2475](https://github.com/realm/realm-core/pull/2475). +* `AppendBuffer` gained support for move construction/assignment, and had its + growth factor reduced to 1.5. + PR [#2462](https://github.com/realm/realm-core/pull/2462). +* Methods on the `Replication` interface were made virtual to allow override. + PR [#2462](https://github.com/realm/realm-core/pull/2462). +* The order of emission for some instructions in the transaction log was changed + with respect to carrying out the effect of the instruction on the database, to + allow implementors of the `Replication` interface a semi-consistent view of + the database. + PR [#2462](https://github.com/realm/realm-core/pull/2462). +* Lock file format bumped from version 9 to 10 due to introduction of + `SharedInfo::history_schema_version`. + PR [#2481](https://github.com/realm/realm-core/pull/2481). +* Removal of obsolete logic and semantics relating to obsolete history type + `Replication::hist_OutOfRealm`. + PR [#2481](https://github.com/realm/realm-core/pull/2481). +* Code specific to history type `Replication::hist_InRealm` (class + `_impl::InRealmHistory` in particular) was moved from + `realm/impl/continuous_transactions_history.hpp` and + `realm/impl/continuous_transactions_history.cpp` to `realm/sync/history.cpp`. + PR [#2481](https://github.com/realm/realm-core/pull/2481). + +---------------------------------------------- + +# 2.4.0 Release notes + +### Bugfixes + +* Fixes a bug in chuncked binary column returning null value. + PR [#2416](https://github.com/realm/realm-core/pull/2416). + Fixes issue [#2418](https://github.com/realm/realm-core/issues/2418). +* Possibly fixed some cases of extreme file size growth, by preventing starvation + when trying to start a write transaction, while simultaneously pinning an older + version. + PR [#2395](https://github.com/realm/realm-core/pull/2395). +* Fixed a bug when deleting a column used in a query. + PR [#2408](https://github.com/realm/realm-core/pull/2408). +* Fixed a crash that occurred if you tried to override a binary with a size close + to the limit. + PR [#2416](https://github.com/realm/realm-core/pull/2416). +* `seekpos()` and `seekoff()` in `realm::util::MemoryInputStreambuf` now behave + correctly when argument is out of range. + PR [#2472](https://github.com/realm/realm-core/pull/2472). + +### Breaking changes + +* The table macros, supporting the typed interface, has been removed. + PR [#2392](https://github.com/realm/realm-core/pull/2392). +* Layout and version change for the .lock file required in order to prevent + starvation when waiting to start a write transaction (see above). + PR [#2395](https://github.com/realm/realm-core/pull/2395). + +### Enhancements + +* Now supports case insensitive queries for UWP. + PR [#2389](https://github.com/realm/realm-core/pull/2389). +* Upgraded Visual Studio project to version 2017. + PR [#2389](https://github.com/realm/realm-core/pull/2389). +* Support handover of TableViews and Queries based on SubTables. + PR [#2470](https://github.com/realm/realm-core/pull/2470). +* Enable reading and writing of big blobs via Table interface. + Only to be used by Sync. The old interface still has a check on + the size of the binary blob. + PR [#2416](https://github.com/realm/realm-core/pull/2416). + +---------------------------------------------- + +# 2.3.3 Release notes + +### Bugfixes + +* Fix a hang in LIKE queries that could occur if the pattern required + backtracking. PR [#2477](https://github.com/realm/realm-core/pull/2477). +* Bug fixed in `GroupWriter::write_group()` where the maximum size of the top + array was calculated incorrectly. This bug had the potential to cause + corruption in Realm files. PR [#2480](https://github.com/realm/realm-core/pull/2480). + +### Enhancements + +* Use only a single file descriptor in our emulation of interprocess condition variables + on most platforms rather than two. PR [#2460](https://github.com/realm/realm-core/pull/2460). Fixes Cocoa issue [#4676](https://github.com/realm/realm-cocoa/issues/4676). + +---------------------------------------------- + +# 2.3.2 Release notes + +### Bugfixes +* Fixed race condition bug that could cause crashes and corrupted data + under rare circumstances with heavy load from multiple threads accessing + encrypted data. (sometimes pieces of data from earlier commits could be seen). + PR [#2465](https://github.com/realm/realm-core/pull/2465). Fixes issue [#2383](https://github.com/realm/realm-core/issues/2383). +* Added SharedGroupOptions::set_sys_tmp_dir() and + SharedGroupOptions::set_sys_tmp_dir() to solve crash when compacting a Realm + file on Android external storage which is caused by invalid default sys_tmp_dir. + PR [#2445](https://github.com/realm/realm-core/pull/2445). Fixes Java issue [#4140](https://github.com/realm/realm-java/issues/4140). + +----------- + +### Internals + +* Remove the BinaryData constructor taking a temporary object to prevent some + errors in unit tests at compile time. PR [#2446](https://github.com/realm/realm-core/pull/2446). +* Avoid assertions in aggregate functions for the timestamp type. PR [#2466](https://github.com/realm/realm-core/pull/2466). + +---------------------------------------------- + +# 2.3.1 Release notes + +### Bugfixes + +* Fixed a bug in handover of detached linked lists. (issue #2378). +* Fixed a bug in advance_read(): The memory mappings need to be updated and + the translation cache in the slab allocator must be invalidated prior to + traversing the transaction history. This bug could be reported as corruption + in general, or more likely as corruption of the transaction log. It is much + more likely to trigger if encryption is enabled. (issue #2383). + +### Enhancements + +* Avoid copying copy-on-write data structures when the write does not actually + change the existing value. +* Improve performance of deleting all rows in a TableView. +* Allow the `add_int()` API to be called on a `Row` +* Don't open the notification pipes on platforms which support the async commit + daemon when async commits are not enabled + +----------- + +### Internals + +* Updated OpenSSL to 1.0.2k. +* Setting environment variable `UNITTEST_XML` to a nonempty value will no longer + disable the normal console output while running the test suite. Instead, in + that case, reporting will happen both to the console and to the JUnit XML + file. + +---------------------------------------------- + +# 2.3.0 Release notes + +### Bugfixes + +* Fixed various bugs in aggregate methods of Table, TableView and Query for nullable columns + (max, min, avg, sum). The results of avg and sum could be wrong and the returned index of + the min and max rows could be wrong. Non-nullable columns might not have been affected. + One of the bugs are described here https://github.com/realm/realm-core/issues/2357 +* Prevent `stdin` from being accidentally closed during `~InterProcessCondVar()`. + +### Breaking changes + +* Attempts to open a Realm file with a different history type (Mobile Platform vs + Mobile Database) now throws an IncompatibleHistories exception instead of a + InvalidDatabase (as requested in issue #2275). + +### Enhancements +* Windows 10 UWP support. Use the new "UWP" configurations in Visual Studio to + compile core as a static .lib library for that platform. Also see sample App + in the uwp_demo directory that uses the static library (compile the .lib first). + Note that it is currently just an internal preview with lots of limitations; see + https://github.com/realm/realm-core/issues/2059 +* Added 'void SharedGroup::get_stats(size_t& free_space, size_t& used_space)' + allowing access to the size of free and used space (Requested in issue #2281). +* Optimized Contains queries to use Boyer-Moore algorithm (around 10x speedup on large datasets) +* Parameter arguments passed to logger methods (e.g., `util::Logger::info()`) + are now perfectly forwarded (via perfect forwarding) to `std::stream::operator<<()`. + +----------- + +### Internals + +* Unit tests now support JUnit output format. + +---------------------------------------------- + +# 2.2.1 Release notes + +### Enhancements + +* Parameter arguments passed to logger methods (e.g., `util::Logger::info()`) + are now perfectly forwarded (via perfect forwarding) to + `std::stream::operator<<()`. + +----------- + +### Internals + +* Make `_impl::make_error_code(_impl::SimulatedFailure::FailureType)` + participate in overload resolution in unqualified ADL contexts like + `make_error_code(_impl::SimulatedFailure::sync_client__read_head)` and `ec == + _impl::SimulatedFailure::sync_client__read_head`. +* `P_tmpdir` should not be used on Android. A better default name for temporary + folders has been introduced. + +---------------------------------------------- + +# 2.2.0 Release notes + +### Bugfixes +* Fix possible corruption of realm file in case of more than 1000 entries in a + link list (#2289, #2292, #2293, #2295, #2301) +* Fixed crash in query if a table had been modified so much that payload array + leafs had relocated (#2269) +* Fix a race involving destruction order of InterprocessMutex static variables. +* Fix a crash when a Query is reimported into the SharedGroup it was exported + for handover from. +* Fix a crash when calling mkfifo on Android 4.x external storage. On 4.x devices, + errno is EPERM instead of EACCES. +* Fix a crash when updating a LinkView accessor from a leaf to an inner node. (#2321) + +### Breaking changes + +* The return type of `util::File::copy()` has been changed from `bool` to + `void`. Errors are now reported via `File::AccessError` exceptions. This + greatly increases the utility and robustness of `util::File::copy()`, as it + now catches all errors, and reports them in the same style as the other + functions in `util::File`. + +### Enhancements + +* Added support for LIKE queries (wildcard with `?` and `*`) +* Offer facilities to prevent multiple sync agents per Realm file access session + (`Replication::is_sync_agent()` to be overridden by sync-specific + implementation). The utilized lock-file flag + (`SharedInfo::sync_agent_present`) was added a long time ago, but the + completion of detection mechanism got postponed until now. +* Improve performance of write transactions which free a large amount of + existing data. +* Added `util::File::compare()` for comparing two files for equality. + +----------- + +### Internals + +* Added extra check for double frees in slab allocator. +* Deprecated Array type parameters in Column and BpTree constructors + +---------------------------------------------- + +# 2.1.4 Release notes + +### Bugfixes + +* Fix storage of very large refs (MSB set) on 32-bit platforms. +* Fixed a race between destruction of a global mutex as part of main thread exit + and attempt to lock it on a background thread, or conversely attempt to lock a + mutex after it has been destroyed. (PR #2238, fixes issues #2238, #2137, #2009) + +---------------------------------------------- + +# 2.1.3 Release notes + +### Bugfixes + +* Deleting rows through a `TableView` generated wrong instructions by way of + `Table::batch_erase_rows()`, which would only be noticed after reapplying the + transaction log to a separate Realm file or via synchronization. + +----------- + +### Internals + +* `array_direct.hpp` added to installed headers. + +---------------------------------------------- + +# 2.1.2 Release notes + +### Bugfixes + +* When adding a nullable column of type Float while other columns existed + already, the values of the new column would be non-null. This is now fixed. + +---------------------------------------------- + +# 2.1.1 Release notes + +### Internals + +* Upgraded to OpenSSL 1.0.2j. + +---------------------------------------------- + +# 2.1.0 Release notes + +### Bugfixes + +* Fix an assertion failure when upgrading indexed nullable int columns to the + new index format. +* Extra SetUnique instructions are no longer generated in the transaction log + when a conflict was resolved locally. + +### Breaking changes + +* The ChangeLinkTargets instruction was a misnomer and has been renamed to + MergeRows. + +----------- + +### Internals + +* Android builds: upgraded to OpenSSL 1.0.1u. +* The behavior of MergeRows (formerly ChangeLinkTargets) has been simplified to + be semantically equivalent to a row swap. + +---------------------------------------------- + +# 2.0.0 Release notes + +### Bugfixes + +* TimestampColumn::is_nullable() could return a wrong value. Also, when adding a new + Float/Double column to a Table with rowcount > 0, the new entries would be non-null + even though the column was created as nullable. +* Update accessors after a change_link_target or set_unique operation, so that users + will have the latest data immediately. Previously this would require manually + refetching the data or looking for the unique key again. + +---------------------------------------------- + +# 2.0.0-rc9 Release notes + +### Internals + +* Use Xcode 7.3.1 to build core for Cocoa + +---------------------------------------------- + +# 2.0.0-rc8 Release notes + +### Bugfixes + +* Fixed a crash related to queries that was introduced in rc7. (#2186) +* Fixed a bug triggered through set unique of primary keys through + the ROS. (#2180) + +----------- + +### Internals + +* Optimized query code on a string column with a search index to address a + performance regression observed in the recent format changes to the + string index (see #2173) + +---------------------------------------------- + +# 2.0.0-rc7 Release notes + +### Bugfixes + +* Fixed a race in the handover machinery which could cause crashes following handover + of a Query or a TableView. (#2117) +* Reversed the decision process of resolving primary key conflicts. Instead of + letting the newest row win, the oldest row will now always win in order to not + lose subsequent changes. + +----------- + +### Breaking changes + +* Changed the format of the StringIndex structure to not recursivly store + strings past a certain depth. This fixes crashes when storing strings + with a long common prefix in an index. This is a file format breaking change. + The file format has been incremented and old Realm files must upgrade. + The upgrade will rebuild any StringIndexes to the new format automatically + so other than the upgrade, this change should be effectivly invisible to + the bindings. (see #2153) + +----------- + +### Internals + +* Removed ("deleted") the default copy constructor for RowBase. This constructor + was used by accident by derived classes, which led to a data race. Said race was + benign, but would be reported by the thread sanitizer. + +---------------------------------------------- + +# 2.0.0-rc6 Release notes + +### Enhancements + +* Added debian packages for Ubuntu 16.04. + +---------------------------------------------- + +# 2.0.0-rc4 Release notes + +### Bugfixes + +* Fixed a bug where find() on a Query constructed from a restricting view + did not correctly return an row index into the underlying table. + (issue #2127) +* Fixed a bug where linked tables were not updated after a table move operation, when + run through the replicator. +* Fixed a bug where moving a column to itself caused a crash. + +### Breaking changes + +* New instruction for `Table::add_int()`, which impacts the transaction log + format. + +### Enhancements + +* Added `Table::add_int()` for implementing CRDT counters. + +---------------------------------------------- + +# 2.0.0-rc3 Release notes + +### Bugfixes + +* Fixed a bug with link columns incorrectly updating on a `move_last_over` + operation when the link points to the same table. +* Fix subspecs not updating properly after a move operation. +* Fixed various crashes when using subtables. The crash will occur when the first column + of the subtable if of type `col_type_Timestamp` or if it is nullable and of type Bool, Int + or OldDateTime. Caused by bad static `get_size_from_ref()` methods of columns. (#2101) +* Fixed a bug with link columns incorrectly updating on a `move_last_over` + operation when the link points to the same table. + +### Breaking changes + +* Refactored the `SharedGroup` constructors and open methods to use a new + `SharedGroupOptions` parameter which stores all options together. +* BREAKING! Until now, a Query would return indexes into a restricting view if such was + present (a view given in the `.where(&view) method`, or it would return indexes into the + Table if no restricting view was present. This would make query results useless if you did + not know whether or not a restricting view was present. This fix make it *always* return + indexes into the Table in all cases. Also, any `begin` and `end` arguments could point into + eitherthe View or the Table. These now always point into the Table. Also see + https://github.com/realm/realm-core/issues/1565 + +### Enhancements + +* Accessors pointing to subsumed rows are updated to the new row rather than detached. + +----------- + +### Internals + +* When creating a `SharedGroup`, optionally allow setting the temporary + directory to when making named pipes fails. This is to fix a bug + involving mkfifo on recent android devices (#1959). +* Bug fixed in test harness: In some cases some tests and checks would be + counted twice due to counters not being reset at all the right times. + +---------------------------------------------- + +# 2.0.0-rc2 Release notes + +### Enhancements + +* Add back log level prefixes for `StderrLogger` and `StreamLogger` + +---------------------------------------------- + +# 2.0.0-rc1 Release notes + +### Breaking changes + +* API Breaking change: Added log level argument to util::Logger::do_log(). + Existing implementations can ignore the argument, or use it to add log level + info to the log output. +* API Breaking change: The WriteLogCollector is no longer available. + To create a history object for SharedGroup, make_in_realm_history() + must now be used instead of make_client_history(). +* The commit logs have been moved into the Realm file. This means we no longer + need the .log_a, .log_b and .log files, significantly reducing the number of + both files and open file handles. This is a breaking change, since versions + without .log files cannot interoperate with earlier versions which still + uses separate .log files. (issues #2065, #1354). +* The version for .lock-file data has been bumped to reflect that this is + an API breaking change. + +### Enhancements + +* Elimination of the .log files also eliminates all locking related to + accessing the .log files, making read-transactions lock-free. +* The critical phase of commits have been reduced significantly in length. + If a process is killed while in the critical phase, any other process + working jointly on the same Realm file is barred from updating the Realm + file until the next session. Reducing the length of the critical phase + reduces the risk of any user experiencing this limitation. + (issues #2065, #1354) + +----------- + +### Internals + +* Added support for very large commit history entries. (issues #2038, #2050) + This also implies an API change (but to the internal API) to the + History::get_changesets() method, which must be taken into account by + any derived classes. +* Support for setting and getting thread names (`util::Thread::set_name()` and + `util::Thread::get_name()`) when the platform supports + it. `util::Thread::set_name()` is now used by the test harness as a help while + debugging. Also, the terminate handler (in `util/terminate.cpp`) writes out + the name of the terminating thread if the name is available. +* Fixed doxygen warnings. + +---------------------------------------------- + +# 2.0.0-rc0 Release notes + +### Internals + +* Changed instruction log format of Set instructions to be more amenable to the +addition of future variants. +* Changed instruction log format of LinkList instructions to include information +about the size of the list in question prior to carrying out the instruction. + +---------------------------------------------- + +# 1.5.1 Release notes + +### Bugfixes + +* Fixed java bug #3144 / Core #2014. Management of Descriptor class was + not thread safe with respect to destruction/creation/management of + accessor tree. Bug could be triggered by destruction of TableView on + one thread, while new TableViews where created on a different thread. +* Fixed incorrect results when updating a backlink TableView after inserting + new columns into the source table. + +---------------------------------------------- + +# 1.5.0 Release notes + +### Bugfixes + +* Fix a race condition that could result in a crash if a `LinkView` was + destroyed while another thread was adjusting accessors on a `LinkListColumn`. +* Fix crashes and general brokenness when constructing a Query, inserting a + column into the queried table, and then running the query. +* Fix crashes and general brokenness when syncing a sorted or distincted + TableView after inserting new columns into the source Table. + +### Breaking changes + +* Added support for sorting and distincting table views through a chain of + links. (#1030) + +### Enhancements + +* Improve performance of sorting on non-nullable columns. +* Improve overall sort performance. + +----------- + +### Internals + +* Updated the header license to prepare for open sourcing the code. + +---------------------------------------------- + +# 1.4.2 Release notes + +### Bugfixes + +* Fix a bug with the local mutex for the robust mutex emulation. +* Reduce the number of file descriptors used in robust mutex emulation, + multi instances of InterprocessMutex share the same descriptor. (#1986) + +---------------------------------------------- + +# 1.4.1 Release notes + +### Bugfixes + +* Fixing util::MemoryInputStream to support tellg() and seekg(). +* Fix truncation of the supplied value when querying for a float or double that + is less than a column's value. +* Workaround for the Blackberry mkfifo bug. + +----------- + +### Internals + +* Removed `realm::util::network` library. +* Removed event loop library. +* Reduced the number of open files on Android. + +---------------------------------------------- + +# 1.4.0 Release notes + +### Breaking changes + +* Throw a logic error (of type `table_has_no_columns`) if an attempt is made to + add rows to a table with no columns. (#1897) +* S: A clear operation is emitted on removal of the last public column of a table. + +---------------------------------------------- + +# 1.3.1 Release notes + +### Bugfixes + +* Add missing locks when access `Table::m_views` which may lead to some java + crashes since java will not guarantee destruction and construction always + happen in the same thread. (#1958) +* Fixed a bug where tableviews created via backlinks were not automatically + updated when the source table changed. (#1950) + +### Breaking changes + +* Throw a logic error (of type `table_has_no_columns`) if an attempt is made to + add rows to a table with no columns. (#1897) +* S: A clear operation is emitted on removal of the last public column of a table. + +### Enhancements + +* Increased the verbosity of some exception error messages to help debugging. + +---------------------------------------------- + +# 1.3.0 Release notes + +### Bugfixes + +* Fix a crash when `Group::move_table()` is called before table accessors are + initialized. (#1939) + +### Breaking changes + +* Sorting with `STRING_COMPARE_CORE` now sorts with pre 1.1.2 ordering. Sorting + with 1.1.2 order is available by using `STRING_COMPARE_CORE_SIMILAR`. (#1947) + +----------- + +### Internals + +* Performance improvements for `LinkListColumn::get_ptr()`. (#1933) + +---------------------------------------------- + +# 1.2.0 Release notes + +### Bugfixes + +* Update table views so that rows are not attached after calling Table::clear() (#1837) +* The SlabAlloctor was not correctly releasing all its stale memory mappings + when it was detached. If a SharedGroup was reused to access a database + following both a call of compact() and a commit() (the latter potentially + by a different SharedGroup), the stale memory mappings would shadow part + of the database. This would look like some form of corruption. Specifically + issues #1092 and #1601 are known to be symptoms of this bug, but issues + #1506 and #1769 are also likely to be caused by it. Note that even though + this bug looks like corruption, the database isn't corrupted at all. + Reopening it by a different SharedGroup will work fine; Only the SharedGroup + that executed the compact() will have a stale view of the file. +* Check and retry if flock() returns EINTR (issue #1916) +* The slabs (regions of memory used for temporary storage during a write transaction), + did not correctly track changes in file size, if the allocator was detached, the + file shrunk and the allocator was re-attached. This scenario can be triggered by + compact, or by copying/creating a new realm file which is then smaller than the + old one when you re-attach. The bug led to possible allocation of overlapping + memory chunks, one of which would then later corrupt the other. To a user this + would look like file corruption. It is theoretically possibly, but not likely, + that the corrupted datastructure could be succesfully committed leading to a real + corruption of the database. The fix is to release all slabs when the allocator + is detached. Fixes #1898, #1915, #1918, very likely #1337 and possibly #1822. + +### Breaking changes + +* Removed the 'stealing' variant of export for handover. It was not a great + idea. It was not being used and required locking which we'd like to avoid. +* S: A concept of log levels was added to `util::Logger`. `util::Logger::log()` + now takes a log level argument, and new shorthand methods were added + (`debug()`, `info()`, `warn()`, ...). All loggers now have a `level_threshold` + property through which they efficiently query for the current log level + threshold. + +### Enhancements + +* Allow SharedGroups to pin specific versions for handover +* Reduced the object-size overhead of assertions. +* Fixed a spelling mistake in the message of the `LogicError::wrong_group_state`. + +----------- + +### Internals + +* Non concurrent tests are run on the main process thread. (#1862) +* S: `REALM_QUOTE()` macro moved from `` to + ``. This also fixes a dangling reference to + `REALM_QUOTE_2()` in ``. +* Minimize the amount of additional virtual address space used during Commit(). + (#1478) +* New feature in the unit test framework: Ability to specify log level + threshold for custom intra test logging (`UNITTEST_LOG_LEVEL`). +* Switch from `-O3` to `-Os` to compile OpenSSL: https://github.com/android-ndk/ndk/issues/110 + +---------------------------------------------- + + +# 1.1.2 Release notes + +### Bugfixes + +* S: In the network API (namespace `util::network`), do not report an error to + the application if system calls `read()`, `write()`, or `accept()` fail with + `EAGAIN` on a socket in nonblocking mode after `poll()` has signalled + readiness. Instead, go back and wait for `poll()` to signal readiness again. + +### Breaking changes + +* Sorting order of strings is now according to more common scheme for special + characters (space, dash, etc), and for letters it's now such that visually + similiar letters (that is, those that differ only by diacritics, etc) are + grouped together. (#1639) + +----------- + +### Internals + +* S: New unit tests `Network_ReadWriteLargeAmount` and + `Network_AsyncReadWriteLargeAmount`. + +---------------------------------------------- + + +# 1.1.1 Release notes + +### Bugfixes + +* Fixed a recently introduced crash bug on indexed columns (#1869) +* Implement `TableViewBase`'s copy-assignment operator to prevent link errors when it is used. +* No longer assert on a "!cfg.session_initiator" in SlabAlloc::attach_file(). This makes issue + #1784 go away, but also removes an option to detect and flag if the ".lock" file is deleted + while a SharedGroup is attached to the file. Please note: Removal of the ".lock" file while + the database is attached may lead to corruption of the database. + +### Enhancements + +* Improve performance of opening Realm files and making commits when using + external writelogs by eliminating some unneeded `fsync()`s. + +---------------------------------------------- + +# 1.1.0 Release notes + +### Bugfixes + +* Fix for #1846: If an exception is thrown from SlabAlloc::attach_file(), it + forgot to unlock a mutex protecting the shared memory mapping. In cases + where the last reference to the memory mapping goes out of scope, it would + cause the assert "Destruction of mutex in use". Fix is to use unique_lock + to ensure the mutex is unlocked before destruction. +* Fix a crash when `Table::set_string_unique()` is called but the underlying + column is actually a StringEnumColumn. +* Fix an assertion failure when combining a `Query` with no conditions with another `Query`. + +### Breaking changes + +* S: Type of completion handler arguments changed from `const H&` to `H` for all + asynchronous operations offered by the networking API (namespace + `util::network`). +* S: `util::network::deadline_timer::async_wait()` no longer declared `noexcept` + (it never should have been). + +### Enhancements + +* Strictly enforce not allowing search indexes to be created on unsupported column types. +* S: Event loop API reworked to more closely align with the `util::network` API, + and to better provide for multiple alternative implementations (not considered + breaking because the event loop API was not yet in use). +* S: Bugs fixed in the POSIX based implementation (not listed under bug fixes + because the event loop API was not yet in use). +* S: A new Apple CoreFoundation implementation of event loop API was added. +* S: Movable completion handler objects are no longer copied by the networking + API (namespace `util::network`). + +----------- + +### Internals + +* Upgrade build scripts to build as C++14 by default. +* Corrected two usages of undefined REALM_PLATFORM_ANDROID to REALM_ANDROID. + This correctly enables Android log output on termination and allows using + robust mutexes on Android platforms. (#1834) + + +---------------------------------------------- + +# 1.0.2 Release notes + +### Internals + +* This is functionally the same as 1.0.1. For Xamarin we now do a specialized + cocoa build with only iOS support and without bitcode. + +---------------------------------------------- + +# 1.0.1 Release notes + +### Bugfixes + +* Fix a situation where a failure during SharedGroup::open() could cause stale + memory mappings to become accessible for later: + In case one of the following exceptions are thrown from SharedGroup::open(): + - "Bad or incompatible history type", + - LogicError::mixed_durability, + - LogicError::mixed_history_type, + - "File format version deosn't match: " + - "Encrypted interprocess sharing is currently unsupported" + Then: + a) In a single process setting a later attempt to open the file would + hit the assert "!cfg.session_initiator" reported in issue #1782. + b) In a multiprocess setting, another process would be allowed to run + compact(), but the current process would retain its mapping of the + old file and attempt to reuse those mappings when a new SharedGroup + is opened, which would likely lead to a crash later. In that case, the + !cfg.session_initiator would not be triggered. + May fix issue #1782. + +**Note: This is a hotfix release built on top of 1.0.0 + +---------------------------------------------- + +# 1.0.0 Release notes + +### Bugfixes + +* Fixed move_last_over() replacing null values for binary columns in the moved + row with zero-length values. + +### Enhancements + +* File operations would previously throw `std::runtime_error` for error cases without a + specialized exception. They now throw `AccessError` instead and include path information. + +----------- + +### Internals + +* Fixed an error in Query_Sort_And_Requery_Untyped_Monkey2 test which would cause + this test to fail sometimes. + +---------------------------------------------- + +# 0.100.4 Release notes + +### Bugfixes + +* Fix queries over multiple levels of backlinks to work when the tables involved have + their backlink columns at different indices. + +### Breaking changes + +* Reverting the breaking changes wrongly introduced by 0.100.3, so that + this release does NOT have breaking changes with respect to 0.100.2 + + +---------------------------------------------- + +# 0.100.3 Release notes (This is a faulty release and should not be used) + +### Bugfixes + +* Fix initialization of read-only Groups which are sharing file mappings with + other read-only Groups for the same path. +* Fix TableView::clear() to work in imperative mode (issue #1803, #827) +* Fixed issue with Timestamps before the UNIX epoch not being read correctly in + the `TransactLogParser`. Rollbacks and advances with such Timestamps would + throw a `BadTransactLog` exception. (#1802) + +### Breaking changes + +* Search indexes no longer support strings with lengths greater than + `Table::max_indexed_string_length`. If you try to add a string with a longer length + (through the Table interface), then a `realm::LogicError` will be thrown with type + `string_too_long_for_index`. Calling `Table::add_search_index()` will now return a + boolean value indicating whether or not the index could be created on the column. If + the column contains strings that exceed the maximum allowed length, then + `Table::add_search_index()` will return false and the index will not be created, but the data + in the underlying column will remain unaffected. This is so that bindings can attempt to + create a search index on a column without knowing the lengths of the strings in the column. + Realm will continue to operate as before on any search index that already stores strings longer + than the maximum allowed length meaning that this change is not file breaking (no upgrade is + required). However, as stated above, any new strings that exceed the maximum length will + not be allowed into a search index, to insert long strings just turn off the search index + (although this could be left up to the user). + +### Enhancements + +* Distinct is now supported for columns without a search index. Bindings no longer + need to ensure that a column has a search index before calling distinct. (#1739) + +----------- + +### Internals + +* Upgrading to OpenSSL 1.0.1t. + +---------------------------------------------- + +# 0.100.2 Release notes + +### Bugfixes + +* Fix handing over an out of sync TableView that depends on a deleted link list or + row so that it doesn't remain perpetually out of sync (#1770). +* Fix a use-after-free when using a column which was added to an existing table + with rows in the same transaction as it was added, which resulted in the + automatic migration from DateTime to Timestamp crashing with a stack overflow + in some circumstances. + +---------------------------------------------- + +# 0.100.1 Release notes + +### Bugfixes: + +* Fix for: The commit logs were not properly unmapped and closed when a SharedGroup + was closed. If one thread closed and reopened a SharedGroup which was the sole + session participant at the time it was closed, while a different SharedGroup opened + and closed the database in between, the first SharedGroup could end up reusing it's + memory mappings for the commit logs, while the later accesses through a different + SharedGroup would operate on a different set of files. This could cause inconsistency + between the commit log and the database. In turn, this could lead to crashes during + advance_read(), promote_to_write() and possibly commit_and_continue_as_read(). + Worse, It could also silently lead to accessors pointing to wrong objects which might + later open for changes to the database that would be percieved as corrupting. (#1762) +* Fix for: When commitlogs change in size, all readers (and writers) must update their + memory mmapings accordingly. The old mechanism was based on comparing the size of + the log file with the previous size and remapping if they differ. Unfortunately, this + is not good enough, as the commitlog may first be shrunk, then expanded back to the + original size and in this case, the existing mechanism will not trigger remapping. + Without remapping in such situations, POSIX considers accesses to the part of the + mapping corresponding to deleted/added sections of the file to be undefined. Consequences + of this bug could be crashes in advance_read(), promote_to_write() or + commit_and_continue_as_read(). Conceivably it could also cause wrong accessor updates + leading to accessors pointing to wrong database objects. This, in turn, could lead + to what would be percieved as database corruption. (#1764) +* S: Assertion was sometimes dereferencing a dangling pointer in + `util::network::buffered_input_stream::read_oper::recycle_and_execute()`. + +### Enhancements: + +* S: `util::bind_ptr<>` extended with capability to adopt and release naked + pointers. +* The `SharedGroup` constructor now takes an optional callback function so bindings can + be notified when a Realm is upgraded. (#1740) + +---------------------------------------------- + +# 0.100.0 Release notes + +### Bugfixes: + +* Fix of #1605 (LinkView destruction/creation should be thread-safe) and most + likely also #1566 (crash below LinkListColumn::discard_child_accessors...) and + possibly also #1164 (crash in SharedGroup destructor on OS X). +* Copying a `Query` restricted by a `TableView` will now avoid creating a dangling + reference to the restricting view if the query owns the view. Dangling references + may still occur if the `Query` does not own the restricting `TableView`. +* Fixed #1747 (valgrind report of unitialized variable). +* Fixed issue with creation of `ArrayIntNull` with certain default values that would + result in an all-null array. (Pull request #1721) + +### API breaking changes: + +* The return value for LangBindHelper::get_linklist_ptr() and the argument + to LangBindHelper::unbind_linklist_ptr has changed from being a 'LinkView*' + into a 'const LinkViewRef&'. +* Fixed a bug, where handing over a TableView based on a Query restricted + by another TableView would fail to propagate synchronization status correctly + (issue #1698) +* Fixed TableViews that represent backlinks to track the same row, even if that row + moves within its table. (Issue #1710) +* Fixed incorrect semantics when comparing a LinkList column with a Row using a + query expression. (Issue #1713) +* Fixed TableViews that represent backlinks to not assert beneath `sync_if_needed` when + the target row has been deleted. +* `TableView::depends_on_deleted_linklist` is now `TableView::depends_on_deleted_object`, + and will also return true if the target row of a `TableView` that represents backlinks + is deleted. (Issue #1710) +* New nanosecond precision `Timestamp` data and column type replace our current `DateTime` + data and column type. (Issue #1476) +* Notice: Due to the new `Timestamp` data and column type a file upgrade will take place. + Read-only Realm files in apps will have to be updated manually. + +### Enhancements: + +* TableView can now report whether its rows are guaranteed to be in table order. (Issue #1712) +* `Query::sync_view_if_needed()` allows for bringing a query's restricting view into sync with + its underlying data source. + +----------- + +### Internals: + +* Opening a Realm file which already has a management directory no longer throws + and catches an exception. +* The r-value constructor for StringData has been removed because StringIndex does not + store any data. This prevents incorrect usage which can lead to strange results. + +---------------------------------------------- + +# 0.99.0 Release notes + +### Breaking changes: + +* Lock file (`foo.realm.lock`) format bumped. +* Moved all supporting files (all files except the .realm file) into a + separate ".management" subdirectory. + +### Bugfixes: + +* S: Misbehavior of empty asynchronous write in POSIX networking API. +* S: Access dangling pointer while handling canceled asynchronous accept + in POSIX networking API. +* Changed group operator== to take table names into account. + +### Enhancements: + +* Multiple shared groups now share the read-only memory-mapping of + the database. This significantly lowers pressure on virtual memory + in multithreaded scenarios. Fixes issue #1477. +* Added emulation of robust mutexes on platforms which do not + provide the full posix API for it. This prevents a situation + where a crash in one process holding the lock, would leave + the database locked. Fixes #1429 +* Added support for queries that traverse backlinks. Fixes #776. +* Improve the performance of advance_read() over transations that inserted rows + when there are live TableViews. +* The query expression API now supports equality comparisons between + `Columns` and row accessors. This allows for link equality + comparisons involving backlinks, and those that traverse multiple + levels of links. + +* S: Adding `util::network::buffered_input_stream::reset()`. + +----------- + +### Internals: + +* Disabled unittest Shared_RobustAgainstDeathDuringWrite on Linux, as + it could run forever. +* Fixed a few compiler warnings +* Disabled unittest Shared_WaitForChange again, as it can still run forever +* New features in the unit test framework: Ability to log to a file (one for + each test thread) (`UNITTEST_LOG_TO_FILES`), and an option to abort on first + failed check (`UNITTEST_ABORT_ON_FAILURE`). Additionally, logging + (`util::Logger`) is now directly available to each unit test. +* New failure simulation features: Ability to prime for random triggering. + +* S: New unit tests: `Network_CancelEmptyWrite`, `Network_ThrowFromHandlers`. + +---------------------------------------------- + +# 0.98.4 Release notes + +### Bugfixes: + +* Copying a `Query` restricted by a `TableView` will now avoid creating a dangling + reference to the restricting view if the query owns the view. Dangling references + may still occur if the `Query` does not own the restricting `TableView`. (#1741) + +### Enhancements: + +* `Query::sync_view_if_needed()` allows for bringing a query's restricting view into sync with + its underlying data source. (#1742) + +**Note: This is a hotfix release built on top of 0.98.3. The above fixes are + not present in version 0.99** + +---------------------------------------------- + +# 0.98.3 Release notes + +### Bugfixes: + +* Fixed TableViews that represent backlinks to not assert beneath `sync_if_needed` when + the target row has been deleted. (Issue #1723) + +**Note: This is a hotfix release built on top of 0.98.2. The above fixes are + not present in version 0.99** + +---------------------------------------------- + +# 0.98.2 Release notes + +### Bugfixes: + +* Fixed TableViews that represent backlinks to track the same row, even if that row + moves within its table. (Issue #1710) +* Fixed incorrect semantics when comparing a LinkList column with a Row using a + query expression. (Issue #1713) + +### API breaking changes: + +* `TableView::depends_on_deleted_linklist` is now `TableView::depends_on_deleted_object`, + and will also return true if the target row of a `TableView` that represents backlinks + is deleted. (Issue #1710) + +### Enhancements: + +* TableView can now report whether its rows are guaranteed to be in table order. (Issue #1712) + +**Note: This is a hotfix release built on top of 0.98.1. The above fixes are + not present in version 0.99 + +---------------------------------------------- + +# 0.98.1 Release notes + +### Bugfixes: + +* Fixed a bug, where handing over a TableView based on a Query restricted + by another TableView would fail to propagate synchronization status correctly + (issue #1698) + +**Note: This is a hotfix release. The above bugfix is not present + in version 0.99 + +---------------------------------------------- + +# 0.98.0 Release notes + +### Enhancements: + +* Added support for queries that traverse backlinks. Fixes #776. See #1598. +* The query expression API now supports equality comparisons between + `Columns` and row accessors. This allows for link equality + comparisons involving backlinks, and those that traverse multiple + levels of links. See #1609. + +### Bugfixes: + +* Fix a crash that occurred after moving a `Query` that owned a `TableView`. + See #1672. + +**NOTE: This is a hotfix release which is built on top of [0.97.4].** + +----------------------------------------------- + +# 0.97.4 Release notes + +### Bugfixes: + +* #1498: A crash during opening of a Realm could lead to Realm files + which could not later be read. The symptom would be a realm file with zeroes + in the end but on streaming form (which requires a footer at the end of the + file instead). See issue #1638. +* Linked tables were not updated properly when calling erase with num_rows = 0 + which could be triggered by rolling back a call to insert with num_rows = 0. + See issue #1652. +* `TableView`s created by `Table::get_backlink_view` are now correctly handled by + `TableView`'s move assignment operator. Previously they would crash when used. + See issue #1641. + +**NOTE: This is a hotfix release which is built on top of [0.97.3].** + +---------------------------------------------- + +# 0.97.3 Release notes + +### Bugfixes: + +* Update table accessors after table move rollback, issue #1551. This + issue could have caused corruption or crashes when tables are moved + and then the transaction is rolled back. +* Detach subspec and enumkey accessors when they are removed + via a transaction (ex rollback). This could cause crashes + when removing the last column in a table of type link, + linklist, backlink, subtable, or enumkey. See #1585. +* Handing over a detached row accessor no longer crashes. + +**NOTE: This is a hotfix release. The above changes are not present in +versions [0.97.2].** + +---------------------------------------------- + +# 0.97.2 Release notes + +### Enhancements: + +* Add more information to IncompatibleLockFile. + +**NOTE: This is a hotfix release. The above changes are not present in +versions [0.97.1].** + +---------------------------------------------- + +# 0.97.1 Release notes + +### Bugfixes: + +* Fix an alignment problem which could cause crash when opening a Realm file + on 32-bit IOS devices. (issue 1558) + +**NOTE: This is a hotfix release. The above bugfixes are not present in +versions [0.97.0].** + +---------------------------------------------- + +# 0.97.0 Release notes + +### Bugfixes: + +* Backlink columns were not being refreshed when the connected link column + updated it's index in the table (insert/remove/move column). This is now + fixed. See issue #1499. +* Backlink columns were always inserted at the end of a table, however on a + transaction rollback in certain cases, backlink columns were removed from + internal (not the end) indices and the roll back should put them back there. + This could cause a crash on rollback and was reported in ticket #1502. +* Bumps table version when `Table::set_null()` called. + `TableView::sync_if_needed()` wouldn't be able to see the version changes + after `Table::set_null()` was called. + (https://github.com/realm/realm-java/issues/2366) +* Fix an assertion failure in `Query::apply_patch` when handing over + certain queries. +* Fix incorrect results from certain handed-over queries. + +### API breaking changes: + +* Language bindings can now test if a TableView depends on a deleted LinkList + (detached LinkView) using `bool TableViewBase::depends_deleted_linklist()`. + See https://github.com/realm/realm-core/issues/1509 and also + TEST(Query_ReferDeletedLinkView) in test_query.cpp for details. +* `LangBindHelper::advance_read()` and friends no longer take a history + argument. Access to the history is now gained automatically via + `Replication::get_history()`. Applications and bindings should simply delete + the history argument at each call site. +* `SharedGroup::get_current_version()`, `LangBindHelper::get_current_version()`, + and `Replication::get_current_version()` were all removed. They are not used + by the Cocoa or Android binding, and `SharedGroup::get_current_version()` was + never supposed to be public. + +### Enhancements: + +* Adds support for in-Realm history of changes (``), but + keeps the current history implementation as the default for now + (``). +* New methods `ReadTransaction::get_version()` and + `WriteTransaction::get_version()` for getting the version of the bound + snapshot during a transaction. + +----------- + +### Internals: + +* Bumps file format version from 3 to 4 due to support for in-Realm history of + changes (extra entries in `Group::m_top`). The bump is necessary due to lack + of forwards compatibility. The changes are backwards compatible, and automatic + upgrade is implemented. +* Adds checks for consistent use of history types. +* Removes the "server sync mode" flag from the Realm file header. This feature + is now superseded by the more powerful history type consistency checks. This + is not considered a file format change, as no released core version will ever + set the "server sync mode" flag. +* The SharedInfo file format version was bumped due to addition of history type + information (all concurrent session participants must agree on SharedInfo file + format version). +* Make it possible to open both file format version 3 and 4 files without + upgrading. If in-Realm history is required and the current file format version + is less than 4, upgrade to version 4. Otherwise, if the current file format + version is less than 3, upgrade to version 3. +* The current file format version is available via + `Allocator::get_file_format_version()`. +* Set Realm file format to zero (not yet decided) when creating a new empty + Realm where top-ref is zero. This was done to minimize the number of distinct + places in the code dealing with file format upgrade logic. +* Check that all session participants agree on target Realm file format for that + session. File format upgrade required when larger than the actual file format. +* Eliminate a temporary memory mapping of the SharedInfo file during the Realm + opening process. +* Improved documentation of some of the complicated parts of the Realm opening + process. +* Introducing `RefOrTagged` value type whan can be used to make it safer to work + with "tagged integers" in arrays having the "has refs" flag. +* New features in the unit test framework: Ability to specify number of internal + repetitions of the set of selected tests. Also, progress reporting now + includes information about which test thread runs which unit test. Also, new + test introduction macro `NO_CONCUR_TEST()` for those tests that cannot run + concurrently with other tests, or with other executions of themselves. From + now on, all unit tests must be able to run multiple times, and must either be + fully thread safe, or must be introduced with `NO_CONCUR_TEST()`. + +---------------------------------------------- + +# 0.96.2 Release notes + +### Bugfixes: + +* `Group::TransactAdvancer::move_group_level_table()` was forgetting some of its + duties (move the table accessor). That has been fixed. +* While generating transaction logs, we didn't always deselect nested + accessors. For example, when performing a table-level operation, we didn't + deselect a selected link list. In some cases, it didn't matter, but in others + it did. The general rule is that an operation on a particular level must + deselect every accessor at deeper (more nested) levels. This is important for + the merge logic of the sync mechanism, and for transaction log reversal. This + has been fixed. +* While reversing transaction logs, group level operations did not terminate the + preceding section of table level operations. Was fixed. +* Table::clear() issues link nullification instructions for each link that did + point to a removed row. It did however issue those instructions after the + clear instruction, which is incorrect, as the links do not exist after the + clear operation. Was fixed. +* `SharedGroup::compact()` does a sync before renaming to avoid corrupted db + file after compacting. + +### Enhancements: + +* Add SharedGroup::get_transact_stage(). + +### Internals: + +* Improve documentation of `Group::move_table()` and `LinkView::move()`. +* Early out from `Group::move_table()` if `from_index == to_index`. This + behaviour agrees with `LinkView::move()` and is assumed by other parts of + core, and by the merge logic of the sync mechanism. +* Convert some assertions on arguments of public `Group`, `Table`, and + `LinkView` methods to throwing checks. +* Align argument naming of `Group::move_table()` and `LinkView::move()`. + +---------------------------------------------- + +# 0.96.1 Release notes + +### API breaking changes: + +* Important for language bindings: Any method on Query and TableView that + depends on a deleted LinkView will now return sane return values; + Query::find() returns npos, Query::find_all() returns empty TableView, + Query::count() returns 0, TableView::sum() returns 0 (TableView created + from LinkView::get_sorted_view). So they will no longer throw + DeletedLinkView or crash. See TEST(Query_ReferDeletedLinkView) in + test_query.cpp for more info. + +### Enhancements: + +* Memory errors caused by calls to mmap/mremap will now throw a specific + AddressSpaceExhausted exception which is a subclass of the previously + thrown std::runtime_error. This is so that iOS and Android language + bindings can specifically catch this case and handle it differently + than the rest of the general std::runtime_errors. +* Doubled the speed of TableView::clear() when parent table has an + indexed column. + +---------------------------------------------- + +# 0.96.0 Release notes + +### Bugfixes: + +* Handing over a query that includes an expression node will now avoid + sharing the expression nodes between `Query` instances. This prevents + data races that could give incorrect results or crashes. + +### Enhancements: + +* Subqueries are now supported via `Table::column(size_t, Query)`. + This allows for queries based on the number of rows in the linked table + that match the given subquery. + +---------------------------------------------- + +# 0.95.9 Release notes + +### Bugfixes: + +* Fixed terminate() being called rather than InvalidDatabase being thrown when + a non-enrypted file that begins with four zero bytes was opened as an + encrypted file. + +---------------------------------------------- + +# 0.95.8 Release notes + +### Bugfixes: + +* Fixed error when opening encrypted streaming-form files which would be + resized on open due to the size not aligning with a chunked mapping section + boundary. + +### API breaking changes: + +* Any attempt to execute a query that depends on a LinkList that has been + deleted from its table will now throw `DeletedLinkView` instead of + segfaulting. No other changes has been made; you must still verify + LinkViewRef::is_attached() before calling any methods on a LinkViewRef, as + usual. + +### Enhancements: + +* Optimized speed of TableView::clear() on an indexed unordered Table. A clear() + that before took several minutes with 300000 rows now takes a few seconds. + +---------------------------------------------- + +# 0.95.7 Release notes + +### Bugfixes: + +* Corrected a bug which caused handover of a query with a restricting + view to lose the restricting view. + +---------------------------------------------- + +# 0.95.6 Release notes + +### Bugfixes: + +* Fixed incorrect initialization of TableViews from queries on LinkViews + resulting in `TableView::is_in_sync()` being incorrect until the first time + it is brought back into sync. +* Fixed `TableView` aggregate methods to give the correct result when called on + a table view that at one point had detached refs but has since been synced. +* Fixed another bug in `ColumnBase::build()` which would cause it to produce an + invalid B+-tree (incorrect number of elements per child in the compact + form). This is a bug that could have been triggered through proper use of our + bindings in their current form. In particular, it would have been triggered + when adding a new attribute to a class that already has a sufficiently large + number of objects in it (> REALM_MAX_BPNODE_SIZE^2 = 1,000,000). +* Fixed a bug in handover of Queries which use links. The bug was incomplete + cloning of the underlying data structure. This bug goes unnoticed as long + as the original datastructure is intact and is only seen if the original + datastructure is deleted or changed before the handed over query is re-executed + +### Enhancements: + +* Added support for handing over TableRefs from one thread to another. + +----------- + +### Internals: + +* Add `test_util::to_string()` for convenience. std::to_string() is not + available via all Android NDK toolchains. +* New operation: ChangeLinkTargets. It replaces all links to one row with + links to a different row. +* Regular assertions (REALM_ASSERT()) are no longer enabled by default in + release mode. Note that this is a reversion back to the "natural" state of + affairs, after a period of having them enabled by default in release mode. The + Cocoa binding was the primary target when the assertions were enabled a while + back, and steps were taken to explicitely disable those assertions in the + Android binding to avoid a performance-wise impact there. It is believed that + the assertions are no longer needed in the Cocoa binding, but in case they + are, the right approach, going forward, is to enable them specifically for the + Cocoa binding. Note that with these changes, the Android binding no longer + needs to explicitely disable regular assertions in release mode. +* Upgraded Android toolchain to R10E and gcc to 4.9 for all architectures. + +---------------------------------------------- + + +# 0.95.5 Release notes + +### Bugfixes: + +* Fixed Row accessor updating after an unordered `TableView::clear()`. +* Fixed bug in `ColumnBase::build()` which would cause it to produce an invalid + (too shallow) B+-tree. This is a bug that could have been triggered through + proper use of our bindings in their current form. In particular, it would have + been triggered when adding a new attribute to a class that already has a + sufficiently large number of objects in it (> REALM_MAX_BPNODE_SIZE^2 = + 1,000,000). + +### Enhancements: + +* New default constructor added to `BasicRowExpr<>`. A default constructed + instance is in the detached state. + +---------------------------------------------- + +# 0.95.4 Release notes + +### Bugfixes: + +* Fixed incorrect handling of a race between a commit() and a new thread + or process opening the database. In debug mode, the race would trigger an + assert "cfg.session_initiator || !cfg.is_shared", in release mode it could + conceivably result in undefined behaviour. +* Fixed a segmentation fault in SharedGroup::do_open_2 +* Fixed a bug en ringbuffer handling that could cause readers to get a wrong + top pointer - causing later asserts regarding the size of the top array, or + asserts reporting mismatch between versions. + +### API breaking changes: + +* Primary key support has been removed. Instead, new instructions have been + introduced: SetIntUnique, SetStringUnique. To implement primary keys, callers + should manually check the PK constraint and then emit these instructions in + place of the regular SetInt and SetString instructions. + +### Enhancements: + +* Added TableView::distinct() method. It obeys TableView::sync_if_needed(). + A call to distinct() will first fully populate the TableView and then perform + a distinct algorithm on that (i.e. it will *not* add a secondary distinct filter + to any earlier filter applied). See more in TEST(TableView_Distinct) in + test_table_view.cpp. + +----------- + +### Internals: + +* Changed `Group::remove_table`, `Group::TransactAdvancer::insert_group_level_table` + and `Group::TransactAdvancer::erase_group_level_table` from _move-last-over_ to + preserve table ordering within the group. + +---------------------------------------------- + +# 0.95.3 Release notes + +### Bugfixes: + +* Reverted what was presumably a fix for a race between commit and opening the database (0.95.2). + +---------------------------------------------- + +# 0.95.2 Release notes + +### Bugfixes: + +* Fixed bug where Query::average() would include the number of nulls in the + result. +* Presumably fixed a race between commit and opening the database. + +### Enhancements: + +* Recycle memory allocated for asynchronous operations in the networking + subsystem (`util::network`). + +---------------------------------------------- + +# 0.95.1 Release notes + +### Bugfixes: +* Fixed bug that would give false search results for queries on integer columns + due to bug in bithacks deep inside Array::find() + +### Enhancements: + +* Added Table::get_version_counter() exposing the versioning counter for the Table +* Add `TableView::get_query()`. + + +---------------------------------------------- + +# 0.95.0 Release notes + +### Bugfixes: + +* When inserting a new non-nullable Binary column to a table that had + *existing* rows, then the automatically added values would become null +* Fixed updating TableViews when applying a transaction log with a table clear. +* Fewer things are copied in TableView's move constructor. +* Prevent spurious blocking in networking subsystem (put sockets in nonblocking + mode even when used with poll/select). +* Fixed the shared group being left in an inconsistent state if the transaction + log observer threw an exception. +* Fixed issue with table accessors not being updated properly, when link columns + were changed (e.g. in Group::remove_table, when the table had link columns). + +### API breaking changes: + +* Use `util::Logger` instead of `std::ostream` for logging during changeset + replay (`Replication::apply_changeset()`). + +### Enhancements: + +* Eliminated use of signals in encryption. This also fixes failures related + to signals on some devices. + +----------- + +### Internals: + +* More checking and throwing of logical errors in `Table::set_binary()` and + `Table::set_link()`. + +---------------------------------------------- + +# 0.94.4 Release notes + +### Bugfixes: + +* Fixed crash in find_all() + +### Enhancements: + +* Queries are no longer limited to 16 levels of grouping. +* New substring operations (ranged insert, erase on values in string columns). +* Adds schema change notification handler API to Group. + +----------- + +### Internals: + +* New operations: Swap rows, move rows, move column, move group level table. +* Changes order of nullify instructions that appeared as a result of erase + to occur in the transaction log before the erase instruction that caused + them. +* New utility class: DirScanner. +* New test utility function: quote. +* New assertion macro: REALM_ASSERT_EX, replacing REALM_ASSERT_n macros. + + +---------------------------------------------- + +# 0.94.3 Release notes + +### Bugfixes: + +* Fixed mremap() fallback on Blackberry. + +---------------------------------------------- + +# 0.94.2 Release notes + +### Bugfixes: + +* Fixed a bug that lead to SharedGroup::compact failing to attach to the newly + written file. + +---------------------------------------------- + +# 0.94.1 Release notes + +### Bugfixes: + +* Fixed a bug in SharedGroup::Compact() which could leave the database in an + inconsistent state. + +### Enhancements: + +* Queries are no longer limited to 16 levels of grouping. + +----------- + +### Internals: + +* Obsolete YAML-based documentation removed. +* Removed `std::` in front integral types (e.g. `size_t`, `int64_t` etc.) + +---------------------------------------------- + +# 0.94.0 Release notes + +### Bugfixes: + +* Fixed a crash bug that could be triggered if a Realm is rapidly opened and + closed and reopened many times on multiple threads. The bug caused the + internal version information structure to overflow, causing an assert or a + crash (if assert was disabled). +* The error handling for `pthread_cond_wait()/pthread_cond_timedwait()` + incorrectly attributed the failure to `pthread_mutex_lock()`. +* The error handling for several File functions incorrectly attributed the + failure to `open()`. +* Added the bitcode marker to iOS Simulator builds so that bitcode for device + builds can actually be used. +* Build with bitcode both enabled and disabled for iOS for compatibility with + Xcode 6. + +### API breaking changes: +* None. + +### Enhancements: +* Supports finding non-null links (Link + LinkList) in queries, using + syntax like `Query q = table->column(col).is_not_null();` +* Comparisons involving unary links on each side of the operator are now + supported by query_expression.hpp. +* Added version chunk information and failure reason for + `pthread_mutex_lock()`. +* Termination routines now always display the library's version before the + error message. +* Automatically clean up stale MemOnly files which were not deleted on close + due to the process crashing. + +----------- + +### Internals: + +* All calls to `REALM_TERMINATE` or `util::terminate()` now display the + library's version. It is no longer necessary to include `REALM_VER_CHUNK` in + calls to those functions. +* Various bug fixes in `util::network`, most notably, asynchronous operations + that complete immediately can now be canceled. +* Improved documentation in `util::network`. +* Improved exception safety in `util::network`. +* `util::network::socket_base::close()` is now `noexcept`. +* New `util::network::socket_base::cancel()`. +* Added `util::network::deadline_timer` class. +* Breaking: Free-standing functions `util::network::write()` and + `util::network::async_write()` converted to members of + `util::network::socket`. + + +---------------------------------------------- + +# 0.93.0 Release notes + +### Bugfixes: +* Fixed severe bug in Array allocator that could give asserts like + `Assertion failed: value <= 0xFFFFFFL [26000016, 16777215]`, especially + for BinaryData columns. This bug could be triggered by using binary data + blobs with a size in the range between 8M and 16M. +* Fixed assert that could happen in rare cases when calling set_null() on an + indexed nullable column. +* Fixed all aggregate methods on Table (min, max, etc) that hadn't been + updated/kept in sync for a long while (null support, return_ndx argument,..). +* Bug in upgrading from version 2 -> 3 (upgrade could be invoked twice for the + same file if opened from two places simultaneously) +* `Spec` and thereby `Descriptor` and `Table` equality has been fixed. Now + handles attributes (nullability etc), sub tables, optimized string columns + and target link types correctly. +* A stackoverflow issue in encrypted_file_mapping. Allocing 4k bytes on the + stack would cause some random crashes on small stack size configurations. +* Now includes a statically-linked copy of OpenSSL crypto functions rather + than dynamically linking Androids system OpenSSL to avoid bugs introduced + by system crypto functions on some devices. +* Added copy constructor to `BasicRow` to fix a bug that could lead to + unregistered row accessors being created. This bug is also part of a list of + blocking issues that prevent the test suite from running when compiled with + `-fno-elide-constructors`. +* A bug in the `Query` copy constructor has been fixed that could cause asserts + due to missing capacity extension in one of the object's internal members. +* `Expression` subclasses now update `Query`s current descriptor after setting + the table. This prevents a null dereference when adding further conditions + to the query. +* Fixes a crash due to an assert when rolling back a transaction in which a link + or linklist column was removed. +* A bug in `Query` copying has been fixed. The bug could cause references to + Tables which should stay under the supervision of one SharedGroup to leak + to another during handover_export() leading to corruption. +* Query expression operators now give correct results when an argument comes + from a link. +* Fixed a bug in the way the new memory mapping machinery interacted with + encryption. +* Query expression comparisons now give correct results when comparing a linked + column with a column in the base table. +* Fixed assertion failure when TableViewBase::is_row_attached() would return + false in a debug build. + +### API breaking changes: + +* A number of methods in the following classes have been renamed to match the + coding guidelines (lowercase, underscore separation): + * `Array`, `ArrayBlob`, `ArrayInteger`, `ArrayString`, `BasicArray`; + * `Column`, `IntegerColumn`, `StringColumn`, `StringEnumColumn`; + * `Group`; + * `Query`; + * `StringIndex`. +* `TableView::remove()`, `TableView::remove_last()`, and `TableView::clear()` + now take an extra argument of type `RemoveMode` which specifies whether rows + must be removed in a way that does, or does not maintain the order of the + remaining rows in the underlying table. In any case, the order of remaining + rows in the table view is maintained. This is listed as an API breaking change + because the situation before this change was confusing, to say the least. In + particular, `TableView::clear()` would choose between the ordered and the + unordered mode based on whether the underlying table had at least one link (or + link list) column. You are strongly advised to revisit all call sites and + check that they do the right thing. Note that both bindings (Cocoa and + Android) are likely to want to use unordered mode everywhere. + +### Enhancements: +* Added argument to Table::average() and TableView::average() that returns number + of values that were used for computing the average +* Full null support everywhere and on all column types. See + `TEST(Query_NullShowcase)` in `test_query.cpp` in core repo. +* Added `Descriptor::get_link_target()`, for completeness. +* Added extra `allow_file_format_upgrade` argument to `SharedGroup::open()`. +* Modifying `Descriptor` methods now throw `LogicError` when appropriate (rather + than asserting). +* Allow querying based on the number of rows that a linked list column links to, + using expressions like `table->column(0).count() > 5`. +* New `util::File::AccessError::get_path()` returns the file system path + associated with the exception. Note that exception classes + `util::File::PermissionDenied`, `util::File::NotFound`, `util::File::Exists`, + and `InvalidDatabase` are subclasses of `util::File::AccessError`. +* Allow queries to include expressions that compute aggregates on columns in linked tables, + such as `table->column(0).column(1).sum() >= 1000`. +* Added a check for functioning SEGV signals to fail more gracefully when + they're broken. + +----------- + +### Internals: + +* Added argument to SharedGroup to prevent automatic file format upgrade. If an + upgrade is required, the constructor will throw `FileFormatUpgradeRequired`. +* Let `LinkColumn` and `LinkListColumn` adhere to the same nullability interface + as the rest of the column types. +* The code coverage CI job now builds with the `-fno-elide-constructors` flag, + which should improve the depth of the coverage analysis. All bugs that were + blocking the use of this flag have been fixed. +* SharedGroup no longer needs to remap the database file when it grows. This is + a key requirement for reusing the memory mapping across threads. +* `NOEXCEPT*` macros have been replaced by the C++11 `noexcept` specifier. +* The `REALM_CONSTEXPR` macro has been replaced by the C++11 `constexpr` keyword. +* Removed conditional compilation of null string support. + +---------------------------------------------- + +# 0.92.3 Release notes + +### Bugfixes: + +* Added the bitcode marker to iOS Simulator builds so that bitcode for device + builds can actually be used. + +**NOTE: This is a hotfix release. The above bugfixes are not present in +versions [0.93.0].** + +---------------------------------------------- + +# 0.92.2 Release notes + +### Bugfixes: + +* Fixed assertion failure when TableViewBase::is_row_attached() would return + false in a debug build. +* Fixes a crash due to an assert when rolling back a transaction in which a link + or linklist column was removed. + +**NOTE: This is a hotfix release.** + +----------- + +### Internals: + +* Now built for Apple platforms with the non-beta version of Xcode 7. + +---------------------------------------------- + +# 0.92.1 Release notes + +### Bugfixes: + +* Reverted prelinking of static libraries on Apple platforms as it caused + `dynamic_cast<>()` and `typeid()` checks to fail in some scenarios, including + when sorting by integer or floating point columns. + +----------- + +### Internals: + +* Renamed `Column` to `IntegerColumn` and `TColumn` to `Column`. +* Renamed `AdaptiveStringColumn` to `StringColumn`. +* Several column classes were renamed to follow the `XxxColumn` naming scheme + (e.g., `ColumnLink` to `LinkColumn`). +* Removed conditional compilation of replication features. +* More information from `InvalidDatabase::what()`. +* Disabled support for the async daemon on iOS and watchOS. + +---------------------------------------------- + +# 0.92.0 Release notes + +### Bugfixes: + +* The upgraded file format version is written out to disk, eliminating potential + deadlocks. + +### API breaking changes: + +* Support for the following deprecated operations on Table has been removed: + insert_int, insert_string, etc., insert_done, and add_int. To insert a value, + one must now call insert_empty_row, then set the appropriate values for each + column. +* Changed `LinkView::move` so that the `new_link_ndx` will be the index at which + the moved link can be found after the move is completed. + +### Enhancements: + +* Support for ordered row removal in tables with links. This was done for + completeness, and because an ordered insertion in tables with links, when + reversed, becomes an ordered removal. Support for ordered insertion in tables + with links was added recently because the sync mechanism can produce + them. Also added a few missing pieces of support for ordered insertion in + tables with links. + +----------- + +### Internals: + +* Added static `Array::create_array()` for creating non-empty arrays, and extend + `Array::create()` such that it can create non-empty arrays. +* The creation of the free-space arrays (`Group::m_free_positions`, + `Group::m_free_lengths`, `Group::m_free_versions`) is now always done by + `GroupWriter::write_group()`. Previously it was done in various places + (`Group::attach()`, `Group::commit()`, `Group::reset_free_space_versions()`). +* `Group::reset_free_space_versions()` has been eliminated. These days the Realm + version is persisted across sessions, so there is no longer any cases where + version tracking on free-space chunks needs to be reset. +* Free-space arrays (`Group::m_free_positions`, `Group::m_free_lengths`, + `Group::m_free_versions`) was moved to `GroupWriter`, as they are now only + needed during `GroupWriter::write_Group()`. This significantly reduces the + "shallow" memory footprint of `Group`. +* Improved exception safety in `Group::attach()`. +* `Group::commit()` now throws instead of aborting on an assertion if the group + accessor is detached or if it is used in transactional mode (via + `SharedGroup`). +* Instruction encoding changed for `InsertEmptyRows` and `EraseRows` (also used + for `move_last_over()`). The third operand is now `prior_num_rows` (the number + of rows prior to modification) in all cases. Previously there was a serious + confusion about this. +* Cleaned up the batch removal of rows used by `TableView`. +* Optimize row removal by skipping cascade mechanism when table has no forward + link columns. +* Virtual `ColumnBase::insert(row_ndx, num_rows, is_append)` was changed to + `ColumnBase::insert_rows(row_ndx, num_rows_to_insert, + prior_num_rows)`. Virtual `ColumnBase::erase(row_ndx, is_last)` was changed to + `ColumnBase::erase_rows(row_ndx, num_rows_to_erase, prior_num_rows)`. Virtual + `ColumnBase::move_last_over(row_ndx, last_row_ndx)` was changed to + `ColumnBase::move_last_row_over(row_ndx, prior_num_rows)`. Function names were + changed to avoid confusing similarity to the various non-virtual operations of + the same name on subclasses of `ColumnBase`. `prior_num_rows` is passed + because if carries more useful information than + `is_append`/`is_last`. `num_rows_to_erase` was added for consistency. +* On some subclasses of `ColumnBase` a new non-virtual `erase(row_ndx, is_last)` + was added for practical reasons; an intended overload of `erase(row_ndx)` for + when you know whether the specified row index is the last one. +* Slight performance improvements in `Array::FindGTE()`. +* Renamed `Array::FindGTE()` to `Array::find_gte()`. + +---------------------------------------------- + +# 0.91.2 Release notes + +### Enhancements: + +* Added support for building for watchOS. + +---------------------------------------------- + +# 0.91.1 Release notes + +### Bugfixes: + +* Fixed a bug in SharedGroup::grab_specific_readlock() which would fail to + grab the specified readlock even though the requested version was available + in the case where a concurrent cleanup operation had a conflicting request + for the same (oldest) entry in the ringbuffer. +* Fixed a performance regression in TableView::clear(). + +### API breaking changes: + +* Argument `is_backend` removed from from the public version of + `SharedGroup::open()`. Fortunately, bindings are not currently calling + `SharedGroup::open()`. +* `File::resize()` no longer calls `fcntl()` with `F_FULLFSYNC`. This feature + has been moved to `File::sync()`. + +### Enhancements: + +* New feature added to disable all forms of 'sync to disk'. This is supposed to + be used only during unit testing. See header `disable_sync_to_disk.hpp`. +* Added `LinkList.swap()` to swap two members of a link list. +* Added a Query constructor that takes ownership of a TableView. + +### Internals: + +* On Linux we now call 'sync to disk' after Realm file resizes. Previusly, this + was only done on Apple platforms. + +---------------------------------------------- + +# 0.91.0 Release notes + +### Bugfixes: + +* Fixed assertion when tests are run with `REALM_OLDQUERY_FALLBACK` disabled by + updating Value::import to work with DateTime +* Fix incorrect results when querying for < or <= on ints which requires 64 bits + to represent with a CPU that supports SSE 4.2. + +### API breaking changes: + +* Named exception UnreachableVersion replaced by "unspecified" LogicError + exception. + +### Enhancements: + +* Generic networking API added. +* Support for transfer/handover of TableViews, Queries, ListViews and Rows + between SharedGroups in different threads. Cooperative handover (where boths + threads participate) is supported for arbitrarily nested TableViews and + Queries. Restrictions apply for non-cooperative handover (aka stealing): user + must ensure that the producing thread does not trigger a modifying operation + on any of the involved TableViews. For TableViews the handover can be one of + *moving*, *copying* or *staying*, reflecting how the actual payload is + treated. +* Support for non-end row insertion in tables with link and link list columns. +* Improved documentation of functions concerning the initiation and termination + of transactions. +* Improved exception safety in connection with the initiation and termination of + transactions. +* Add support for systems where mremap() exists but fails with ENOTSUP. + +### Internals: + +* New facility for simulating failures, such as system call failures. + +---------------------------------------------- + +# 0.90.0 Release notes + +### API breaking changes: + +* Merged lr_nulls into master (support for null in String column and bugfix in +String index with 0 bytes). If you want to disable all this again, then #define +REALM_NULL_STRINGS to 0 in features.h. Else API is as follows: Call add_column() +with nullable = true. You can then use realm::null() in place of any +StringData (in Query, Table::find(), get(), set(), etc) for that column. You can +also call Table::is_null(), Table::set_null() and StringData::is_null(). This +upgrades the database file from version 2 to 3 initially the first time a file +is opened. NOTE NOTE NOTE: This may take some time. It rebuilds all indexes. + +---------------------------------------------- + +# 0.89.9 Release notes + +### Bugfixes: + +* The check for functioning SEGV signals threw the exception only once. Now it +always throws when trying to use encryption. + +**NOTE: This is a hotfix release. The above bugfixes are not present in +versions [0.90.0, 0.92.1].** + +---------------------------------------------- + +# 0.89.8 Release notes + +### Enhancements: + +* Added a check for functioning SEGV signals to fail more gracefully when + they're broken. + +**NOTE: This is a hotfix release. The above bugfixes are not present in +versions [0.90.0, 0.92.1].** + +---------------------------------------------- + +# 0.89.7 Release notes + +### Bugfixes: + +* A stackoverflow issue in encrypted_file_mapping. Allocing 4k bytes on the + stack would cause some random crashes on small stack size configurations. +* Now includes a statically-linked copy of OpenSSL crypto functions rather + than dynamically linking Androids system OpenSSL to avoid bugs introduced + by system crypto functions on some devices. + +**NOTE: This is a hotfix release. The above bugfixes are not present in +versions [0.90.0, 0.92.1].** + +---------------------------------------------- + +# 0.89.6 Release notes + +### Bugfixes: + +* Fixed durability issue in case of power / system failures on Apple + platforms. We now use a stronger synchronization (`fcntl(fd, F_FULLFSYNC)`) to + stable storage when the file is extended. + +---------------------------------------------- + +# 0.89.5 Release notes + +### Bugfixes: + +* Fixed errors when a changes to a table with an indexed int column are rolled + back or advanced over. + +---------------------------------------------- + +# 0.89.4 Release notes + +### Enhancements: + +* Detaching (and thus destroying) row acessors and TableViews can now be done + safely from any thread. +* Improved performance of Query::find_all() with assertions enabled. + +---------------------------------------------- + +# 0.89.3 Release notes + +### Bugfixes: + +* Fixed LinkViews containing incorrect data after a write transaction + containing a table clear is rolled back. +* Fixed errors when a changes to a table with an indexed int column are rolled + back. + +### Enhancements: + +* Changes the mmap doubling treshold on mobile devices from 128MB to 16MB. +* SharedGroup::compact() will now throw a runtime_error if called in detached state. +* Make the start index of `ListView::find()` overrideable for finding multiple + occurances of a given row in a LinkList. +* Add `Group::set_cascade_notification_handler()` to simplify tracking changes + due to link nullification and cascading deletes. + +### Internals: + +* Can now be built with encryption enabled on Linux. + +---------------------------------------------- + +# 0.89.1 Release notes + +### Bugfixes: + +* Fixed bug in "index rebuilding" (would delete the wrong column, causing + crash). See https://github.com/realm/realm-core/pull/798 ; "Remove the correct + column when removing search indexes #798" + +---------------------------------------------- + +# 0.89.0 Release notes + +### Bugfixes: + +* Added a check for NUL bytes in indexed strings to avoid corrupting data + structures. +* Fixed bug in SharedGroup::compact(). The bug left the freelist outdated in + some situations, which could cause crash later, if work continued on the same + shared group. The bug did not affect the data written to the compacted + database, but later commits working on the outdated freelist might have. The + fix forces proper (re)initialization of the free list. +* Fixed incorrect results in querying on an indexed string column via a + LinkView. +* Fixed corruption of indexes when using move_last_over() on rows with + duplicated values for indexed properties. + +### API breaking changes: + +* Changed the `tightdb` namespace to `realm`. +* We switched to C++11, and removed functionality that was duplicated from the + C++11 standard library, including `null_ptr` and `util::UniquePtr`. + +### Enhancements: + +* Improved performance of advance_read() over commits with string or binary data + insertions. +* Improved performance sorting TableView and LinkView. +* Added Table::remove_search_index(). + +---------------------------------------------- + +# 0.88.6 Release notes + +### Bugfixes: + +* Fixed bug in Integer index that could make it crash or return bad results + (String index not affected) + +---------------------------------------------- + +# 0.88.5 Release notes + +### Bugfixes: + +* Fixed crash when `std::exit()` is called with active encrypted mappings. +* Fixed writing over 4KB of data to an encrypted file with `Group::write()`. +* Fixed crash after making commits which produced over 4KB of writelog data with + encryption enabled. + +----------- + +### Internals: + +* Switched to using mach exceptions rather than signal() for encrypted mappings + on Apple platforms. + +---------------------------------------------- + +# 0.88.4 Release notes + +### Bugfixes: + +* Fixed out-of-bounds reads when using aggregate functions on sorted TableViews. +* Fixed issues with ArrayString that *could* be the cause of all the asserts the + past few days + +----------- + +### Internals: + +* Many `REALM_ASSERT` invocations replaced by new `REALM_ASSERT_3` macro + that prints variable/argument contents on failure. It's not implemented + optimally yet. + +---------------------------------------------- + +# 0.88.3 Release notes + +### Enhancements: + +* Added emulation of inter-process condition variables for use on platforms which + do not properly implement them. + +---------------------------------------------- + +# 0.88.2 Release notes + +### Bugfixes: + +* Fixed duplicated results when querying on a link column with matches at row + 1000+. + +----------- + +### Internals: + +* Added support for Android ARM64 + +---------------------------------------------- + +# 0.88.1 Release notes + +### Bugfixes: + +* Fix encryption on platforms with non-4KB pages. + +---------------------------------------------- + +# 0.88.0 Release notes + +### Enhancements: + +* SharedGroup::compact() now appends ".tmp_compaction_space" to the database + name in order to get the name of its temporary workspace file instead of + ".tmp". It also automatically removes the file in case it already exists + before compaction. +* Add support for comparing string columns to other string columns in queries. +* `WriteTransaction::has_table()` and `WriteTransaction::rollback()` were + added. Previously, only implicit rollback was possible with + `WriteTransaction`. + +----------- + +### Internals: + +* All assert failures now print the release version number. + +---------------------------------------------- + +# 0.87.6 Release notes + +### Bugfixes: + +* Fixed a crashbug which could cause a reading thread to miss accessor updates + during advance_read(), if the pending updates consisted of two or more empty + commits followed by one or more non-empty commit. The left out accessor + updates could lead to inconsistent datastructures which could presumably later + cause database corruption. + +### Enhancements: + +* Adding *null* support to `BinaryData` in exactly the same way as it was added + to `StringData`. + +---------------------------------------------- + +# 0.87.5 Release notes + +### Bugfixes: + +* `AdaptiveStringColumn::find_all()` with an index no longer returns its results + twice. +* Fixed `Table::get_distinct_view()` on tables which have not been modified + since they were loaded. + +### Enhancements: + +* Added `SharedGroup::wait_for_change_release()` which allows you to release a + thread waiting inside wait_for_change() on a specific SharedGroup instance. +* SharedGroup now allows you to coordinate which version of the data a read + transaction can see. The type `VersionID` represents a specific commit to the + database. A user can obtain the `VersionID` for the active transaction from + `SharedGroup::get_version_of_current_transaction()`, and use it to obtain a a + transaction accessing that same version from another ShareGroup. This is done + by new forms of `SharedGroup::begin_read()`, `SharedGroup::advance_read()`. + Operators are provided so that VersionID's may be compared. +* Creating distinct views on integer, datetime, bool and enum columns is now + possible. +* Add `Table::minimum_datetime()` and `Table::maximum_datetime()`. +* Extending `Table::get_sorted_view()` to support multi-column sorting. + +----------- + +### Internals: + +* Now uses system OpenSSL on Android rather than a statically-linked copy for + encryption. + +---------------------------------------------- + +# 0.87.4 Release notes + +### Bugfixes: + +* Fixed a crash when calling get_sorted_view() on an empty LinkList. + +---------------------------------------------- + +# 0.87.3 Release notes + +### Bugfixes: + +* Fixed bug in String and Integer index where find()/find_all() would return a + wrong match. +* Fixed the values of `Table::max_string_size`, and `Table::max_binary_size`. +* Fixed a bug occuring when claring a table with a search index on a string + column with many rows (>1000). + +---------------------------------------------- + +# 0.87.2 Release notes + +### Internals: + +* Extra assertions in `src/realm/util.file.cpp`. + +---------------------------------------------- + +# 0.87.1 Release notes + +### Enhancements: + +* Added 'compact' method to SharedGroup for better control of compaction of the + database file. +* The following constants were added: `Group::max_table_name_length`, + `Table::max_column_name_length`, `Table::max_string_size`, and + `Table::max_binary_size`. +* Now throwing on overlong table and column names, and on oversized strings and + binary data values. +* Fall back to the old query nodes for String as well as int/double/float. +* Log assertions failures to the native logging system on android and Apple. + +----------- + +### Internals: + +* There is now three kinds of runtime assertions, `REALM_ASSERT_DEBUG()`, + which is retained only in debug-mode builds, `REALM_ASSERT_RELEASE()`, which + is also retained in release-mode builds, and finally, `REALM_ASSERT()`, + which is normally only retained in debug-mode builds, but may occasionally be + retained in release-mode builds too, depending on the specific build + configuration. +* `REALM_ASSERT()` assertions are now enabled in release-mode builds by + default. + +---------------------------------------------- + +# 0.87.0 Release notes + +### API breaking changes: + +* `TransactLogRegistry` is no longer available and must therefore no longer be + passed to `LangBindHelper::advance_read()` and + `LangBindHelper::promote_to_write()`. +* The exceptions `PresumablyStaleLockFile` and `LockFileButNoData` are no longer + thrown from SharedGroup and has been removed from the API. + +### Enhancements: + +* Support for implicit transactions has been extended to work between multiple + processes. +* Commitlogs can now be persisted and support server-synchronization + +---------------------------------------------- + +# 0.86.1 Release notes + +### Enhancements: + +* Added `SharedGroup::get_number_of_versions()` which will report the number of + distinct versions kept in the database. +* Added support for encryption +* Adding `SharedGroup::wait_for_change()` which allows a thread to sleep until + the database changes. + +---------------------------------------------- + +# 0.86.0 Release notes + +### Bugfixes: + +* Fixed a bug where rollback of an empty transaction could cause a crash. + +### API breaking changes: + +* Table::erase() can no longer be used with unordered tables. Previously it was + allowed if the specified index was the index of the last row in the table. One + must now always use Table::move_last_over() with unordered tables. Whether a + table is ordered or unordered is entirely decided by the way it is used by the + application, and note that only unordered tables are allowed to contain link + columns. + +### Enhancements: + +* TableView::sync_if_needed() now returns a version number. Bindings can compare + version numbers returned in order to determine if the TableView has changed. +* Added not_equal(), equal(), contains(), begins_with(), ends_with() for String + columns in the Query expression syntax. They work both case sensitive and + insensitive. So now you can write 'size_t m = + table1->column(0).contains("A", true).find();'. Works with Links too. +* Major simplification of ".lock" file handling. We now leave the ".lock" file + behind. +* Support added for cascading row removal. See `Descriptor::set_link_type()` for + details. All previsouly created link columns will effectively have link-type + 'weak'. +* Rows can now be removed via a row accessors (`Row::remove()`, + `Row::move_last_over()`). +* Speedup of double/float conditions in query expression of a factor ~5 (uses + fallback to old query nodes for double/float too, instead of only for integer + conditions). + +---------------------------------------------- + +# 0.85.1 Release notes + +### Bugfixes: + +* Made Query store a deep copy of user given strings when using the expression + syntax + +---------------------------------------------- + +# 0.85.0 Release notes + +### Bugfixes: + +* Fixed a crash when copying a query checking for equality on an indexed string + column. +* Fixed a stack overflow when too many query conditions were combined with Or(). + +### API breaking changes: + +* Now supports index on Integer, Bool and Date columns; API is the same as for + String index +* `Query::tableview()` removed as it might lead to wrong results - e.g., when + sorting a sorted tableview. + +### Enhancements: + +* Make the durability level settable in the `SharedGroup` constructor and + `open()` overloads taking a `Replication`. + +---------------------------------------------- + +# 0.84.0 Release notes + +### API breaking changes: + +* `Table::set_index()` and `Table::has_index()` renamed to + `Table::add_search_index()` and `Table::has_search_index()` respectively, and + `Table::add_search_index()` now throws instead of failing in an unspecified + way. +* `Table::find_pkey_string()` replaces `Table::lookup()` and has slightly + different semantics. In particular, it now throws instead of failing in an + unspecified way. + +### Enhancements: + +* A row accessor (`Row`) can now be evaluated in boolean context to see whether + it is still attached. +* `Table::try_add_primary_key()` and `Table::remove_primary_key()` added. +* `Table::find_pkey_int()` added, but not yet backed by an integer search index. +* Added method `LangBindHelper::rollback_and_continue_as_read()`. This method + provides the ability to rollback a write transaction while retaining + accessors: Accessors which are detached as part of the rolled back write + transaction are *not* automatically re-attached. Accessors that were attached + before the write transaction and which are not detached during the write + transaction will remain attached after the rollback. + +----------- + +### Internals: + +* Introducing `LogicError` as an alternative to expected exceptions. See + https://github.com/Realm/realm/wiki/Exception-safety-guarantees for more + on this. +* Various query related speed improvements. +* Test suite now passes ASAN (address sanitizer). + +---------------------------------------------- + +# 0.83.1 Release notes + +### Bugfixes: + +* Fixed bug where a TableView generated from a LinkViewRef did not update when + the origin or target table changed. + +---------------------------------------------- + +# 0.83.0 Release notes + +### API breaking changes: + +* Sorting on LinkView and TableView by multiple columns: Both classes now have + get_sorted_view() (returns sorted view) and sort() (in-place sort). Both + methods can take either a single column index as argument (as size_t) or a + std::vector of columns to sort by multiple columns. +* You can now query a LinkView by calling Query::where(link_view.get()).... See + TEST(LinkList_QueryOnLinkList) in test_link_query_view.cpp for an example. + *** IMPORTANT NOTE: Do not call sort() on a LinkView because it does not + yet support replication ***. get_sorted_view() works fine though. + +----------- + +### Internals: + +* Made common base class for TableView and LinkView with common shared + functionality (so far just sort). + +---------------------------------------------- + +# 0.82.3 Release notes + +### Bugfixes: + +* Fixed bug in deep copy of Query, causing the experienced crash at end of scope + of a Query after add_constraint_to_query() had been executed. The fix may not + be optimal as it limits nesting of group/end_group to 16 levels, and also + makes Query take 128 extra bytes of space. Asana task has been made. + +* Fixed bug that would cause `Group::commit()` and + `LangBindHelper::commit_and_continue_as_read()` to fail in the presence of + search indexes. + +* Bugfix: Replication::m_selected_link_list was not cleared. This bug could lead + to general corruption in cases involving link lists. + +---------------------------------------------- + +# 0.82.2 Release notes + +### Internals: + +* Query must now be deep-copied using the '=' operator instead of using + TCopyExpressionTag. Also fixed a bug in this deep-copying. + +---------------------------------------------- + +# 0.82.1 Release notes + +### Internals: + +* `REALM_MAX_LIST_SIZE` was renamed to `REALM_MAX_BPNODE_SIZE`. `BPNODE` + stands for "B+-tree node". +* `REALM_MAX_BPNODE_SIZE` now defaults to 1000 in both *release* and *debug* + mode. + +---------------------------------------------- + +# 0.82.0 Release notes + +### API breaking changes: + +* `Group::has_table()` removed, because it had awkward and incongruous + semantics, and could not be efficiently implemented. +* The version of `Group::get_table()`, that takes a name argument, can no longer + add a table to the group, instead it returns null if there is no table with + the spaecified name. Addition is now handled by either `Group::add_table()` or + `Group::get_or_add_table()`. +* `Group::get_table()` and Group::get_table_name() now throw + `realm::InvalidArgument` if the specified table index is out of range. +* Template version of `Group::get_table()` now throws `DescriptorMismatch` if + the dynamic type disagrees with the statically specified custom table type. +* `LangBindHelper::bind_table_ref()` was renamed to + `LangBindHelper::bind_table_ptr()`, and `LangBindHelper::unbind_table_ref()` + to `LangBindHelper::unbind_table_ptr()`. +* LangBindHelper functions such as get_table() have been updated to reflect the + changed Group API. +* Exception type `ResourceAllocError` eliminated, as there was no good reason + for keeping it (it had no clear role). + +### Enhancements: + +* `Group::find_table()` added as a way of mapping a table name to the index of + table in the group. +* `Group::add_table()` and `Group::get_or_add_table()` were added. +* `Group::remove_table()` and `Group::rename_table()` were added. +* `WriteTransaction::add_table()` and `WriteTransaction::get_or_add_table()` + ware added. + +---------------------------------------------- + +# 0.81.0 Release notes + +### API breaking changes: + +* `Table::get_parent_row_index()` and `Table::get_index_in_group()` together + replace `Table::get_index_in_parent()`. This was done to avoid a confusing mix + of distinct concepts. + +### Enhancements: + +* It's now possible to sort a LinkRef according to a column in the target + table. Also lets you build a TableView with the sorted result instead. The new + methods on LinkViewRef are `sort()` and `get_sorted_view()` + +---------------------------------------------- + +# 0.80.5 Release notes + +### Bugfixes: + +* Fixed bug in `where(&tv)...find()` where it would fail to find a match, if + usig with a TableView, tv. +* Fixed bug in `Table::remove()` which would leak memory when rows were removed + and the table was a link target. +* Fixed bug that prevented reuse of free-space when using + `LangBindHelper::promote_to_write()` and + `LangBindHelper::commit_and_continue_as_read()`. + +### Enhancements: + +* Lets you search for null-links, such as + `table2->column(col_link2).is_null().find()`. Works for `Link` and + `LinkedList`. + +----------- + +### Internals: + +* `Group::Verify()` now checks that the managed memory is precisely the + combination of the recorded free space and the used space reachable from the + root node. + +---------------------------------------------- + +# 0.80.4 Release notes + +### Bugfixes: + +* Bug fixed that would always leave a link list accessor (`LinkView`) in a + corrupt state after a call to `Group::commit()` or + `LangBindHelper::commit_and_continue_as_read()`, if the link list was modified + during the ended "transaction", and was non-empty either before, after, or + both before and after that "transaction". + +----------- + +### Internals: + +* Efficiency of CRUD operations has been greatly improved due to an improvement + of SlabAlloc). The cost of end-insertion (MyTable::add()), for example, has + been reduced to less than a 10th of its previous cost. + +---------------------------------------------- + +# 0.80.3 Release notes + +### Bugfixes: + +* Fixed bug in `Table::add_column()` which would produce a corrupt underlying + node structure if the table already contains more than N**2 rows, where N is + `REALM_MAX_LIST_SIZE` (currently set to 1000). +* Fixed bugs in `Table::clear()` which would produce a corrupt underlying node + structure if the table already contains more than N rows, where N is + `REALM_MAX_LIST_SIZE` (currently set to 1000). + +### Enhancements: + +* Lets you find links that point at a specific row index. Works on Query and + Table. Please see `LinkList_QueryFindLinkTarget` in `test_link_query_view.cpp` + for usage. + +----------- + +### Internals: + +* Table::Verify() has been heavily extended and now also checks link columns and + link lists (debug mode only). + +---------------------------------------------- + +# 0.80.2 Release notes + +### Bugfixes: + +* Fixed bug causing corrupted table accessor when advancing transaction after last regular column is removed from table with remaining hidden backlink columns. +* Fixed replication issue causing too many link list selection instructions to be generated. + +---------------------------------------------- + +# 0.80.1 Release notes + +### Bugfixes: + +* Fixed several bugs in connection to removal of like-type columns. +* Fixed bug when last regular column is removed from table with remaining hidden backlink columns. +* Fixed bug causing corrupted table accessor when column are added or removed before alink column. + +---------------------------------------------- + +# 0.80.0 Release notes + +### Bugfixes: + +* Fixed bug in `TableView::clear()` causing crash if its table contained link columns. +* Fixed bug which would corrupt subtable accessors when inserting or removing parent table columns. +* Fixed bug in LinkView::refresh_accessor_tree() causing problems when transaction is advanced after a link list is cleared. +* Fixed bug causing problems when transaction is advanced after a table with link-like columns is cleared. +* Fixed bug in connection with cyclic link relationships. + +### Enhancements: + +* Added methods `LinkView::remove_target_row()` and `LinkView::remove_all_target_rows()`. +* Support for removing link columns + + +---------------------------------------------- + +# 0.23.0 Release notes + +### Bugfixes: +* Fixed bug in `TableView::remove()` causing crash or undefined behavior. +* Fixed bugs in `Table::insert_column()` and `Table::remove_column()` causing crash or undefined behaviour. +* Fixed corruption bug when a string enumeration column follows a column with attached search index (index flavor mixup). +* Fixed in `Array::erase()` causing crash in certain row insertion scenarios. +* Fixed bug in enumerated strings column (corruption was possible when inserting default values). +* Fixed bug in `Table::update_from_parent()` causing a crash if `Group::commit()` in presence of generated subtable accessor. +* Fixed several link-related bugs due to confusion about the meaning of `LinkView::m_table`. + +### API breaking changes: + +* Views can now be be kept synchronized with changes to the tables used to generate the view, use `TableView::sync_if_needed()` to do so. Views are no longer detached when the table they have been generated from are changed. Instead they just go out of sync. See further description in `src/realm/table_view.hpp`. +* is_attached(), detach(), get_table(), and get_index() moved from BasicRow to RowFuncs. This makes it possible to write `link_list[7].get_index()`, for instance. +* `LinkView::get_target_row(link_ndx)` was removed as it is now just a shorthand for the equally efficient `LinkView::get(link_ndx).get_index()`. +* Added missing const versions of `LinkView::get()` and `LinkView::operator[]()`. +* Confusing `LinkView::get_parent()` removed. +* Added `LinkView::get_origin_table()` and `LinkView::get_target_table()`. + +### Enhancements: +* Now maximum() and minimum() can return the index of the match and not only the value. Implemented for Query, Table and TableView. +* Now supports links in Table::to_json. Please see unit tests in the new test_json.cpp file +* Now supports DateTime Query::maximum_datetime() and DateTime Query::minimum_datetime() +* Supports links in queries, like `(table1->link(3).column(0) > 550).find()`. +* Added support for links and lists of links as column types, to enable relationships between tables. +* Adding `Table::get_index_in_parent()` and `Group::get_table(std::size_t table_ndx)`. They were needed for implicit transactions. +* `Table::get_parent_table()` can now also return the index of the column in the parent. +* Support for row accessors. +* Table, row, and descriptor accessors are now generally retained and properly adjusted when the parent table is modified. +* Added methods to find rows by target in TableView and LinkView. + + +----------- + +### Internals: + +* TableView now creates and stores a deep-copy of its query, in order for the view to refresh itself + +---------------------------------------------- + +0.5.0 Release notes (2014-04-02) +================================ + +C++ (core) +----------- +The C++ API has been updated and your code will break! + +### Bugfixes: + +* None. + +### API breaking changes: + +* None. + +### Enhancements: + +* Read transactions are now non-blocking and their overhead has been reduced by an order of magnitude. + +----------- + +### Internals: + +* New test suite with support for parallelized testing replaces UnitTest++. See section 'Testing' in `README.md`. + + +---------------------------------------------- + + +Realm Changelog: +================== + +Format: + +2012-mm-dd +---------- +! Fixed bug [github issuenr]: .... (user visible bug fixed - passed on to release notes) ++ Added feature .... (user visible new feature - passed on to release notes) +- Removed/deprecated feature/method .... (user visible removed feature - passed on to release notes) +. Any other notes .... (internal changes) + + +2014-05-14 (Lasse Reinhold) ++ Lets you sort a TableView according to a Float, Double or String column (only integral column types possible before) + + +2014-05-08 (Finn Schiermer Andersen) ++ Added negation to the query engine. + + +2014-04-01 (Kristian Spangsege) ++ New framework with support for parallelized unit testing replaces UnitTest++. See section 'Testing' in `README.md`. + + +2014-03-25 (Kristian Spangsege) +! Fixed bug when clearing table with a float/double column. + + +2014-03-13 (Finn Schiermer Andersen) +! Fixed missing initialization of empty columns in some scenarios. + + +2014-02-19 (Finn Schiermer Andersen) +! Fixed space leak in group_writer. Could in some scenarios cause massive increase in database file size. + + +2014-02-17 (Kristian Spangsege) ++ Adding Table::write() as a means to effieciently serialize a table, or part of a table. +! Fixing table copy bug. The error occured when the table contained strings longer than 64 bytes. +! Fixing table comparison bug. The error occured when the table has a `float` or a `double` column. + + +2014-02-14 (Kristian Spangsege) + +* New test suite with support for parallelized testing replaces UnitTest++. See section 'Testing' in `README.md`. ++ The StringData class now distinguishes `null` from the empty string. ++ Adding StringData::is_null(). + + +2014-02-11 (Kristian Spangsege) ++ Group::write(std::ostream&) added. This allows general online streaming of Realm databases in memory for the first time. ++ Adding Table::get_name() which returns the name of the table when the table is a direct member of a group. + + +2014-02-05 (Kenneth Geisshirt) ++ Two new targets in build.sh: get_version and set_version. + + +2014-02-04 (Kristian Spangsege) ++ Introducing Table::get_parent_table() with allows a subtable to know its parent table. ++ Table::rename_subcolumn() and Table::remove_subcolumn() now take an extra argument which is the index of the column to rename. Before the index was specified as part of the path. ++ Introducing Table::insert_column() and Table::insert_subcolumn() for inserting new columns at arbitrary positions. ++ New class `Descriptor` introduced into the public API. It plays the role that `Spec` played before. Class `Spec` is no longer to be considered part of the public API. ++ Table::add_column() now takes a third optinal argument `DescriptorRef* subdesc`. ++ Introducing Table::get_descriptor() and Table::get_subdescriptor() +- Table::get_spec() and Table::update_from_spec() removed from public API since are part of the now non-public `Spec` API. +. Table::has_shared_spec() renamed to Table::has_shared_type(). + + +2014-01-27 (Brian Munkholm) +! Fixed bug in Query with subtables. Whith empty subtables query returned incorrect results. + In debug mode it could assert when querying a subtable with more columns than the base table. + +2014-01-23 (Kenneth Geisshirt) +! Fixed bug: Subtable queries is validated by Query::validate(). An invalid subtable query can lead to a segfault. + + +2014-01-07 (Kenneth Geisshirt) ++ Table::range() added. The method returns a set of rows as a TableView. + + +2014-01-06 (Kristian Spangsege) +! 'No parent info in shared specs' conflicts with implementation of `Group::commit()`. +! `ColumnTable::m_spec_ref` not updated when Spec object is reallocated. +! `ColumnSubtableParent::m_index` not updated when preceeding columns are removed. ++ Addition of `template std::size_t Table::add_subcolumn(const util::Tuple&, DataType, StringData)`. This makes it much easier to add columns to subtable specs. +. `Spec::get_subtable_spec()` now returns `SubspecRef` or `ConstSubspecRef`. This fixes a design bug relating to propagation of constness to subspecs, and it improves the efficiency of access to subspecs by avoiding expensive copying of `Spec` objects. +. Elimination of `Spec::m_table` and `ColumnTable::m_spec_ref`. +. `Spec::add_column()` and `Spec::add_subcolumn()` now take a `Table*` as argument. + + +2013-12-17 (Kristian Spangsege) ++ Implicit termination of active transaction when SharedGroup is destroyed. +. Class `File` and related exceptions such as `File::AccessError` moved to namespace `realm::util`. +. Table::add_column() optimized. For integer columns, the speedup is by more than a factor of 1000 for large tables. + + +2013-11-07 (Alexander Stigsen) +. BREAKING CHANGE: Schema now handles attributes separately for better performance when there are many colummns. + + +2013-11-07 (Lasse Reinhold) ++ Added power() operator for next-generation-queries + + +2013-11-07 (Lasse Reinhold) +! Fixed bug: ng-queries could segfault to bug in Array::get_chunk(). Normal queries and everything else not affected. + + +2013-10-10 (Kenneth Geisshirt) +. Adding INTERACTIVE mode for the dist-config target in build.sh + + +2013-10-09 (Kenneth Geisshirt) +. Adding dist-deb target in build.sh for building debian/ubuntu/mint packages. Moreover, the ubuntu/mint version is part of package name so maintaining repositories is easier. + + +2013-09-26 (Brian Munkholm) ++/- Renamed Table::distinct() to Table::get_distinct_view() ++/- Renamed class Date to DateTime. Renamed DataType::type_Date to type_DateTime ++/- Renamed suffix of all methods operating on DateTime from '_date' to '_datetime'. + + +2013-09-26 (Kristian Spangsege) ++ File format support for streaming of output from Group::write() (not yet suported by API.) ++ Support for internal null characters in strings. This applies to table and column names as well. + + +2013-09-19 (Kristian Spangsege) +. CRUD performance has been greatly improved for large tables, as long as they are kept on the "compact" form. A table is kept on the compact form when every row insertion and removal, since the creation of that table, has occured, and continues to occur at the end (i.e., insert after last row, and remove last row). + + +2013-10-02 (Lasse Reinhold) +- Renamed find_next(lastmatch) into find(begin_at_table_row) for queries and typed tables. + + +2013-09-12 (Brian Munkholm) ++ Added TableView::row_to_string() and testcases for to_string() and row_to_string() ++ Added row_to_string(), to_string() and to_json() in typed TableView. + + +2013-08-31 (Kristian Spangsege) ++ Database files are now exanded by doubling the size until it reaches 128 MiB. After that, it is expanded in chunks of 128 MiB. Until now, it was always expanded in chunks of 1 MiB. + + +2013-08-28 (Kristian Spangsege) ++ Table::is_valid() renamed to Table::is_attached(). The notion of accessor attachment describes much better what is going on than the notion of validity. + + +2013-08-23 (Kristian Spangsege) ++ Stop throwing from destructors (all), and from SharedGroup::rollback() and SharedGroup::end_read(). ++ General stability and error checking improvements. +! Fixed many bugs relating to Group::commit(). +! Fixed some bugs relating to SharedGroup::commit(). +! Fixed bug in TableViewBase::sort(). + + +2013-08-18 (Kenneth Geisshirt) +! Group::to_string() formatting was incorrect. See https://app.asana.com/0/1441391972580/5659532773181. + + +2013-08-03 (Kristian Spangsege) ++ Table::find_sorted_int() replaced by Table::lower_bound_int() and Table::upper_bound_int() as these are standardized and provide more flexibility. ++ Addition of Table::lower_bound_bool() and Table::upper_bound_bool(). ++ Addition of Table::lower_bound_float() and Table::upper_bound_float(). ++ Addition of Table::lower_bound_double() and Table::upper_bound_double(). ++ Addition of Table::lower_bound_string() and Table::upper_bound_string(). They rely on simple byte-wise lexicographical comparison. No Unicode or locale dependent collation is taken into account. Comparison occurs exactly as defined by std::lexicographical_compare in the C++ STL. + + +2013-07-19 (Dennis Fantoni) ++ Added Table::set_subtable(size_t column_ndx, size_t row_ndx, const Table*) + + +2013-06-25 (Kristian Spangsege) +. The default group open mode has been changed from Group::mode_Normal + (read/write) to Group::mode_ReadOnly. This makes it possible to open + a read-only file without specifying a special open mode. Also, since + changed groups are generally written to new files, there is rarely a + need for the group to be opened in read/write mode. +. Group::mode_Normal has been renamed to Group::mode_ReadWrite since it is no longer a normal mode. +. Group::mode_NoCreate has been renamed to Group::mode_ReadWriteNoCreate for clarity. + + +2013-06-05 (Kristian Spangsege) +. Group::write(path) now throws File::Exists if 'path' already exists in the file system. + + +2013-05-16 (Kristian Spangsege) ++ New SharedGroup::reserve() method added. + + +2013-05-13 (Kenneth Geisshirt) +. Added "uninstall" target in build.sh for simple uninstallation. + + +2013-05-07 (Kristian Spangsege) +. Exception File::OpenError renamed to File::AccessError. This affects + various methods in Group and SharedGroup. + + +2013-04-23 (Kristian Spangsege) ++ Support for explicit string lengths added. Most method arguments and + return values of type 'const char*' have been changed to be of type + 'StringData'. This new type is defined in + . 'StringData' can be implicitly + constructed from 'const char*', so no change is required when + passing arguments. Source code change is required when dealing with + returned strings of type 'const char*'. The following is a complete + list: + Affected form Possible replacement + --------------------------------------------------------------------------- + group.get_table_name(...) group.get_table_name(...).data() + table.get_column_name() table.get_column_name().data() + table.get_string(...) table.get_string(...).data() + table.get_mixed(...).get_string() table.get_mixed(...).get_string().data() + table[7].string_col table[7].string_col.c_str() ++ Added support for table[7].string_col.data() and table[7].string_col.size(). ++ Full and seamless interoperability with std::string. This comes + about from the fact that StringData can be implicitly constructed + from, and convert to std::string. ++ Full support for BinaryData in queries. ++ Added BinaryData::data(), BinaryData::size(), BinaryData::operator[]() ++ Added BinaryData::operator==(), BinaryData::operator!=(), BinaryData::operator<() ++ Added BinaryData::begins_with(), BinaryData::ends_with(), BinaryData::contains() ++ Allow BinaryData to be constructed from fixed size array: + template explicit BinaryData(const char (&)[N]) +- BinaryData::pointer removed, use BinaryData::data() instead. +- BinaryData::len removed, use BinaryData::size() instead. +- BinaryData::compare_payload() removed, use BinaryData::operator==() instead. ++ The methods + Table::set_binary(std::size_t column_ndx, std::size_t row_ndx, const char* data, std::size_t size) + Table::insert_binary(std::size_t column_ndx, std::size_t row_ndx, const char* data, std::size_t size) + Table::find_first_binary(std::size_t column_ndx, const char* data, std::size_t size) + Table::find_all_binary(std::size_t column_ndx, const char* data, std::size_t size) + TableView::set_binary(std::size_t column_ndx, std::size_t row_ndx, const char* data, std::size_t size) + have been changed to + Table::set_binary(std::size_t column_ndx, std::size_t row_ndx, BinaryData) + Table::insert_binary(std::size_t column_ndx, std::size_t row_ndx, BinaryData) + Table::find_first_binary(std::size_t column_ndx, BinaryData) + Table::find_all_binary(std::size_t column_ndx, BinaryData) + TableView::set_binary(std::size_t column_ndx, std::size_t row_ndx, BinaryData) + The following changes have been made in the statically + typed API: + Affected form Possible replacement + --------------------------------------------------------- + table[7].binary_col.pointer() table[7].binary_col.data() + table[7].binary_col.len() table[7].binary_col.size() + These changes were made for consistency with StringData. ++ Added Date::operator<() ++ Return type changed from 'std::time_t' to 'Date' on the following + methods: + Mixed::get_date() + Table::get_date() + TableView::get_date() + Argument type changed from 'std::time_t' to 'Date' on many methods including these: + Mixed::set_date() + Table::set_date() + Table::insert_date() + TableView::set_date() + Changes corresponding to these have been made in the statically + typed API. These are some of the affected forms: + time_t(table[7].date_col) + table[7].date_col = val + table[7].mixed_col.get_date() + These changes were made for consistency, and to improve the + isolation of the implementation of 'Date' (it is likely that the + implementation of 'Date' will change). 'Date' can be implicitly + constructed from std::time_t, but it cannot be implicitly converted + to std::time_t (nor is it considered desireable to allow such an + implicit conversion). This means that applications will be affected + as follows: + Affected form Possible replacement + --------------------------------------------------------------------------- + table.get_date(...) table.get_date(...).get_date() + table.get_mixed(...).get_date() table.get_mixed(...).get_date().get_date() + time_t(table[7].date_col) Date(table[7].date_col).get_date() + table[7].date_col.get() table[7].date_col.get().get_date() ++ Group::write() and Group::write_to_mem() are now 'const'. ++ Group::BufferSpec eliminated. Using BinaryData instead. + + +2013-03-11 (Kristian Spangsege) ++ On Linux and OS X, installed shared libraries now carry a platform + dependent API version which is computed from a platform neutral + version specifier (same as GNU Libtool). This allows for multiple + versions of the shared library to be concurrently installed. + + +2013-02-24 (Kristian Spangsege) ++ Adding copy constructors for Table and BasicTable. ++ Adding Table::copy(), BasicTable::copy() and LangBindHelper::copy_table(). ++ Adding BasicTable::create() for symmetry with Table::create(). + + +2013-02-21 (Brian Munkholm +-+ Renamed Group::get_table_count() to Group::size() + + +2013-02-19 (Kristian Spangsege) ++ Type of Group::BufferSpec::m_data changed from to . + + +2013-02-06 (Kristian Spangsege) ++ New enum DataType replaces ColumnType throughout the public API. + + +2013-01-27 (Kristian Spangsege) ++ New Group::Group(unattached_tag) added. Same for SharedGroup. ++ New Group::open(...) methods added. Same for SharedGroup. ++ New Group::is_attached() added. Same for SharedGroup. ++ Classes ReadTransaction and WriteTransaction added for handling safe scoped transaction. ++ Many methods have now been qualified with REALM_NOEXCEPT. + + +2013-01-14 (Kristian Spangsege) +- Group::set_shared() removed. +- SharedGroup::is_valid() removed. Errors are now reported as exceptions from the constructor. + + +2013-01-11 (Kristian Spangsege) ++ Simplified open-mode for Group constructor. +- Group::is_valid() removed. Errors are now reported as exceptions from the constructor. ++ Now using Group::BufferSpec to pass a memory buffer to the Group constructor. ++ Group::write_to_mem() now returns a Group::BufferSpec. ++ Addition of 'bool no_create' arguemnt to SharedGroup constructor. + + +2013-01-08 (Kristian Spangsege) ++ Mixed::set_int() added (same for other value types except subtables). ++ Removed two-argument Mixed constructor for binary data since its signature is expected to be used for strings that are not zero-terminated. + + +2013-01-08 (Brian Munkholm) +---------- ++ New: Added a bunch of methods to support two new column types: float and double. + + +2012-12-16 (Kristian Spangsege) +---------- ++ my_table[i].foo.get() added for all column types except subtables. This is to avoid having to repeat the explicit column type in cast expressions when the actual value is needed. ++ my_table[i].foo.set(...) added for all column types except subtables. This is for completeness. ++ When passing a file name to a Group or a SharedGroup constructor, the type is now a std::string. This is made possible by allowing exception handling. It simplifies the implementation in a few places, and in general it simplifies application code. ++ A 'tag' argument has ben added to the Group constructor that takes a memory buffer as argument. Without this change, two Group constructors were way too similar. + + +2012-12-06 (Brian Munkholm) +---------- ++ 16 New Table:get_string_length(col_ndx, row_ndx) added in Dynamic Table. Missing in Typed Table. + + +2012-12-06 (Kristian Spangsege) +---------- +. "C" API moved to its own repository "realm_c". + + +2012-12-03 (Brian Munkholm) +---------- ++ 15 Updated Group() constructor to take an optional 3'rd parameter 'take_ownership', which allows the caller to keep owenership of the provided data: Group::Group(const char* buffer, size_t len, bool take_ownership=true). + + +2012-11-13 (Kristian Spangsege) +---------- ++ 14 Renamed Table::count() to Table::count_int() + + +2012-11-21 +---------- ++ Added ShareGroup::has_changed() to detect if there has been changes to the db since last transaction. + + +2012-11-12 (Kristian Spangsege) +---------- +! Fixed a memory leak when using Table::create() + + +2012-10-24 (Kristian Spangsege) +---------- ++ 13 Added Table::has_shared_spec(). + + +2012-10-10 (Kristian Spangsege) +---------- +! Fix a problem with MyTable::Query copy constructor that caused access to deallocated memory due to a pointer that was not updated. + + +2012-10-02 (Kristian Spangsege) +---------- ++ New program 'realm-config'. Use it to query about the CFLAGs and/or LDFLAGs to use when linking agains the Realm core library. + + +2012-10-01 (Brian Munkholm) +---------- ++ 12 Added double Table::average(size_t column_ndx) const + + +2012-09-07 (Alexander Stigsen) +---------- ++ File format updated with bigger header and reordered column type [BREAKING] ++ Index is now enabled for string columns (handles string_enum columns as well). ++ 11 Added Table::count_string(size_t column_ndx, const char* target) const; ++ 11 Added Table accessor size_t count(const char* target) const to highlevel interface ++ 11 Spec::add_column(...) now takes an optional parameter for attribute (like indexed). ++ 11 Added Table::to_string() and Group::to_string() for prettified string dump. + + +2012-08-14 (Brian Munkholm) +---------- ++ 10 Renamed FindAllMulti() to find_all_multe(). And SetThreads() to set_threads() ++ 10 Renamed cols() to column(). ++ 10 Renamed get_subspec() to get_subtable_spec(). ++ 10 Renamed parent() to end_subtable(). + + +2012-08-01 (Kristian Spangsege) +---------- ++ 9 Date::operator==(const Date&) and Date::operator!=(const Date&) added. ++ 9 BinaryData::operator==(const BinaryData&) and BinaryData::operator!=(const BinaryData&) added. ++ 9 Operators added for comparison between a value of type Mixed and a value of one of the possible types that a Mixed can contain. Operators are added for both orders of the two arguments. ++ 8 Comparison operators added for "foo" == my_table[i].str and "foo" != my_table[i].str. We already had a comparison operator for the reverse order case, my_table[i].str == "foo". ++ 7 my_table[i].mixed.get_subtable_size() added. + + +2012-07-27 (Kristian Spangsege) +---------- ++ 6 realm::is_a(const Table&) added. ++ 6 realm::unchecked_cast(TableRef) added. ++ 6 realm::checked_cast(TableRef) added. ++ 6 my_table[i].mixed.set_subtable() added. ++ 6 my_table[i].mixed.set_subtable() added. ++ 6 my_table[i].mixed.is_subtable() added (inefficient, do we want it at all?). ++ 6 my_table[i].mixed.get_subtable() added (unsafe/unchecked, do we want it at all?). + + +2012-07-24 (Kristian Spangsege) +---------- ++ New macro REALM_DEBUG to control compilation mode. + The library (including all headers) is no longer affected in any way by the definition status of NDEBUG or _DEBUG. + When we (Realm) compile the library in debug mode, we must define this macro. + We will deliver two versions of the library, one for release mode, and one for debug mode. + If the customer wishes to use the debugging version of the library, he must do two things: + 1) Define REALM_DEBUG in any translation unit that includes a Realm header. + 2) Use the version of the library that is compiled for debug mode (librealm_d.a). ++ 5 Removed obsolete constructor Mixed(ColumnType). Use Mixed(subtable_tag) instead, since this has no runtime overhead. + + +2012-07-19 (Kristian Spangsege) +---------- ++ 4 Table::create() added. Use this to create a freestanding top-level table with dynamic lifetime (determined by reference counting). ++ TableRef::reset() added to set a table reference to null. + + +2012-07-15 (Kristian Spangsege) +---------- ++ 3 Spec::compare() renamed to Spec::operator==(), and made generally available, not only while compiling in debug mode. ++ 3 Spec::operator!=() added. ++ 3 Table::compare() renamed to Table::operator==(), and made generally available, not only while compiling in debug mode. ++ 3 Table::operator!=() added. ++ 3 MyTable::compare() renamed to MyTable::operator==(), and made generally available, not only while compiling in debug mode. ++ 3 MyTable::operator!=() added. ++ 3 Group::operator==() and Group::operator!=() added. +. Array::Compare() and Column::Compare() made generally available, not only while compiling in debug mode. + + +2012-07-09 (Kristian Spangsege) +---------- ++ 1 Table::is_valid() added. Most language bindings must check this flag before calling any member function on any table. ++ 1 MyTable::is_valid() added. ++ See documentation for Table::is_valid() for more details on when a table becomes invalid, and when it does not. ++ Destroying a Group will invalidate all table wrappers (instances of Table) as well as all direct and indirect subtable wrappers. ++ Any modifying operation on a table will generally invalidate all direct and indirect subtable wrappers. ++ 2 my_table[i].mixed.is_subtable() added. ++ 2 my_table[i].mixed.get_subtable() added. + + +2012-07-08 (Kristian Spangsege) +---------- +. LangBindHelper::new_table() now returns null on memory allocation error. This may change in the future to instead throw an exception. + + +2012-06-27 +---------- +-+ Table::sorted(...) changed name to get_sorted_view(...) +- Removed Table::find_pos_int(...) from public API + ++ Added a the following methods to a TableView: + template void set_enum(size_t column_ndx, size_t row_ndx, E value); + ColumnType get_mixed_type(size_t column_ndx, size_t row_ndx) const; + size_t get_subtable_size(size_t column_ndx, size_t row_ndx) const; + void clear_subtable(size_t column_ndx, size_t row_ndx); + size_t find_first_bool(size_t column_ndx, bool value) const; + size_t find_first_date(size_t column_ndx, time_t value) const; + void add_int(size_t column_ndx, int64_t value); + TableView find_all_bool(size_t column_ndx, bool value); + ConstTableView find_all_bool(size_t column_ndx, bool value) const; (for class TableView and ConstTableView) + TableView find_all_date(size_t column_ndx, time_t value); + ConstTableView find_all_date(size_t column_ndx, time_t value) const; (for class TableView and ConstTableView) + +2012-06-?? +---------- +- Group() interfaced changed. Now with multiple options. default option changed from readonly... ++ Generated C++ highlevel API for tables with up to 15 columns diff --git a/src/vendor-include/realm-ios/doc/realm/LICENSE b/src/vendor-include/realm-ios/doc/realm/LICENSE new file mode 100644 index 000000000..02277e87f --- /dev/null +++ b/src/vendor-include/realm-ios/doc/realm/LICENSE @@ -0,0 +1,204 @@ +TABLE OF CONTENTS + +1. Apache License version 2.0 +2. 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. + + END OF TERMS AND CONDITIONS + +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/src/vendor-include/realm-ios/download-realm.lock b/src/vendor-include/realm-ios/download-realm.lock new file mode 100644 index 000000000..8a93cda6f --- /dev/null +++ b/src/vendor-include/realm-ios/download-realm.lock @@ -0,0 +1,3 @@ +SYNC_SERVER_FOLDER=sync +SYNC_ARCHIVE=realm-sync-cocoa-5.0.3.tar.gz +SYNC_ARCHIVE_ROOT=core diff --git a/src/vendor-include/realm-ios/include/realm.hpp b/src/vendor-include/realm-ios/include/realm.hpp new file mode 100644 index 000000000..5e5217365 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm.hpp @@ -0,0 +1,30 @@ +/************************************************************************* + * + * 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 +#include + +#endif // REALM_HPP diff --git a/src/vendor-include/realm-ios/include/realm/alloc.hpp b/src/vendor-include/realm-ios/include/realm/alloc.hpp new file mode 100644 index 000000000..2f6bf6aa7 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/alloc.hpp @@ -0,0 +1,519 @@ +/************************************************************************* + * + * 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 +#include +#include +#include +#include + +namespace realm { + +class Allocator; + +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() const; + ref_type get_ref() const; + 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; + + void set_read_only(bool ro) + { + m_is_read_only = ro; + } + /// 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 + + struct MappedFile; + +protected: + constexpr static int section_shift = 26; + + std::atomic m_baseline; // Separation line between immutable and mutable refs. + + ref_type m_debug_watch = 0; + + // The following logically belongs in the slab allocator, but is placed + // here to optimize a critical path: + + // The ref translation splits the full ref-space (both below and above baseline) + // into equal chunks. + struct RefTranslation { + char* mapping_addr; +#if REALM_ENABLE_ENCRYPTION + util::EncryptedFileMapping* encrypted_mapping; +#endif + }; + // This pointer may be changed concurrently with access, so make sure it is + // atomic! + std::atomic m_ref_translation_ptr; + + /// 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, char* addr, size_t old_size, size_t new_size) = 0; + + /// Release the specified chunk of memory. + virtual void do_free(ref_type, char* addr) = 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; + size_t get_section_index(size_t pos) const noexcept; + inline size_t get_section_base(size_t index) const noexcept; + + + // The following counters are used to ensure accessor refresh, + // and allows us to report many errors related to attempts to + // access data which is no longer current. + // + // * storage_versioning: monotonically increasing counter + // bumped whenever the underlying storage layout is changed, + // or if the owning accessor have been detached. + // * content_versioning: monotonically increasing counter + // bumped whenever the data is changed. Used to detect + // if queries are stale. + // * instance_versioning: monotonically increasing counter + // used to detect if the allocator (and owning structure, e.g. Table) + // is recycled. Mismatch on this counter will cause accesors + // lower in the hierarchy to throw if access is attempted. + std::atomic m_content_versioning_counter; + + std::atomic m_storage_versioning_counter; + + std::atomic m_instance_versioning_counter; + + inline uint_fast64_t get_storage_version(uint64_t instance_version) + { + if (instance_version != m_instance_versioning_counter) { + throw LogicError(LogicError::detached_accessor); + } + return m_storage_versioning_counter.load(std::memory_order_acquire); + } + + inline uint_fast64_t get_storage_version() + { + return m_storage_versioning_counter.load(std::memory_order_acquire); + } + + inline void bump_storage_version() noexcept + { + m_storage_versioning_counter.fetch_add(1, std::memory_order_acq_rel); + } + + inline uint_fast64_t get_content_version() noexcept + { + return m_content_versioning_counter.load(std::memory_order_acquire); + } + + inline void bump_content_version() noexcept + { + m_content_versioning_counter.fetch_add(1, std::memory_order_acq_rel); + } + + inline uint_fast64_t get_instance_version() noexcept + { + return m_instance_versioning_counter.load(std::memory_order_relaxed); + } + + inline void bump_instance_version() noexcept + { + m_instance_versioning_counter.fetch_add(1, std::memory_order_relaxed); + } + +private: + bool m_is_read_only = false; // prevent any alloc or free operations + + friend class Table; + friend class ClusterTree; + friend class Group; + friend class WrappedAllocator; + friend class ConstObj; + friend class Obj; + friend class ConstLstBase; +}; + + +class WrappedAllocator : public Allocator { +public: + WrappedAllocator(Allocator& underlying_allocator) + : m_alloc(&underlying_allocator) + { + m_baseline.store(m_alloc->m_baseline, std::memory_order_relaxed); + m_debug_watch = 0; + m_ref_translation_ptr.store(m_alloc->m_ref_translation_ptr); + } + + ~WrappedAllocator() + { + } + + void switch_underlying_allocator(Allocator& underlying_allocator) + { + m_alloc = &underlying_allocator; + m_baseline.store(m_alloc->m_baseline, std::memory_order_relaxed); + m_debug_watch = 0; + m_ref_translation_ptr.store(m_alloc->m_ref_translation_ptr); + } + + void update_from_underlying_allocator(bool writable) + { + switch_underlying_allocator(*m_alloc); + set_read_only(!writable); + } + +private: + Allocator* m_alloc; + MemRef do_alloc(const size_t size) override + { + auto result = m_alloc->do_alloc(size); + bump_storage_version(); + m_baseline.store(m_alloc->m_baseline, std::memory_order_relaxed); + m_ref_translation_ptr.store(m_alloc->m_ref_translation_ptr); + return result; + } + virtual MemRef do_realloc(ref_type ref, char* addr, size_t old_size, size_t new_size) override + { + auto result = m_alloc->do_realloc(ref, addr, old_size, new_size); + bump_storage_version(); + m_baseline.store(m_alloc->m_baseline, std::memory_order_relaxed); + m_ref_translation_ptr.store(m_alloc->m_ref_translation_ptr); + return result; + } + + virtual void do_free(ref_type ref, char* addr) noexcept override + { + return m_alloc->do_free(ref, addr); + } + + virtual char* do_translate(ref_type ref) const noexcept override + { + return m_alloc->translate(ref); + } + + virtual void verify() const override + { + m_alloc->verify(); + } +}; + + +// 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() const +{ +#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() const +{ +#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) +{ + if (m_is_read_only) + throw realm::LogicError(realm::LogicError::wrong_transact_state); + 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 + if (m_is_read_only) + throw realm::LogicError(realm::LogicError::wrong_transact_state); + return do_realloc(ref, const_cast(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 + REALM_ASSERT(!m_is_read_only); + + return do_free(ref, const_cast(addr)); +} + +inline void Allocator::free_(MemRef mem) noexcept +{ + free_(mem.get_ref(), mem.get_addr()); +} + +inline size_t Allocator::get_section_base(size_t index) const noexcept +{ + return index << section_shift; // 64MB chunks +} + +inline size_t Allocator::get_section_index(size_t pos) const noexcept +{ + return pos >> section_shift; // 64Mb chunks +} + +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.load(std::memory_order_relaxed); +} + +inline Allocator::Allocator() noexcept +{ + m_content_versioning_counter = 0; + m_storage_versioning_counter = 0; + m_instance_versioning_counter = 0; + m_ref_translation_ptr = nullptr; +} + +inline Allocator::~Allocator() noexcept +{ +} + +inline char* Allocator::translate(ref_type ref) const noexcept +{ + if (auto ref_translation_ptr = m_ref_translation_ptr.load(std::memory_order_acquire)) { + char* base_addr; + size_t idx = get_section_index(ref); + base_addr = ref_translation_ptr[idx].mapping_addr; + size_t offset = ref - get_section_base(idx); + auto addr = base_addr + offset; +#if REALM_ENABLE_ENCRYPTION + realm::util::encryption_read_barrier(addr, NodeHeader::header_size, + ref_translation_ptr[idx].encrypted_mapping, + NodeHeader::get_byte_size_from_header); +#endif + return addr; + } + else + return do_translate(ref); +} + +} // namespace realm + +#endif // REALM_ALLOC_HPP diff --git a/src/vendor-include/realm-ios/include/realm/alloc_slab.hpp b/src/vendor-include/realm-ios/include/realm/alloc_slab.hpp new file mode 100644 index 000000000..3b98a5427 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/alloc_slab.hpp @@ -0,0 +1,805 @@ +/************************************************************************* + * + * 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 +#include +#include +#include + +namespace realm { + +// Pre-declarations +class Group; +class GroupWriter; + +namespace util { +struct SharedFileInfo; +} + +/// 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; + bool disable_sync = 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. + /// + /// 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. + /// + /// 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; + + bool is_file_on_streaming_form() const + { + const Header& header = *reinterpret_cast(m_data); + return is_file_on_streaming_form(header); + } + + /// Attach this allocator to an empty buffer. + /// + /// 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); + +#ifdef REALM_DEBUG + /// Deprecated method, only called from a unit test + /// + /// WARNING: This method is NOT thread safe on multiple platforms; see + /// File::prealloc(). + /// + /// 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. + /// + /// 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); +#endif + + /// 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. + /// + /// Updates the memory mappings to reflect a new size for the file. + /// Stale mappings are retained so that they remain valid for other threads, + /// which haven't yet seen the file size change. The stale mappings are + /// associated with a version count if one is provided. + /// They are later purged by calls to purge_old_mappings(). + /// The version parameter is subtly different from the mapping_version obtained + /// by get_mapping_version() below. The mapping version changes whenever a + /// ref->ptr translation changes, and is used by Group to enforce re-translation. + void update_reader_view(size_t file_size); + void purge_old_mappings(uint64_t oldest_live_version, uint64_t youngest_live_version); + + /// Get an ID for the current mapping version. This ID changes whenever any part + /// of an existing mapping is changed. Such a change requires all refs to be + /// retranslated to new pointers. The allocator tries to avoid this, and we + /// believe it will only ever occur on Windows based platforms, and when a + /// compatibility mapping is used to read earlier file versions. + uint64_t get_mapping_version() + { + return m_mapping_version; + } + + /// 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; + + /// Returns the amount of memory requested by calls to SlabAlloc::alloc(). + size_t get_commit_size() const + { + return m_commit_size; + } + + /// Returns the total amount of memory currently allocated in slab area + size_t get_allocated_size() const noexcept; + + /// Returns total amount of slab for all slab allocators + static size_t get_total_slab_size() noexcept; + + /// Hooks used to keep the encryption layer informed of the start and stop + /// of transactions. + void note_reader_start(const void* reader_id); + void note_reader_end(const void* reader_id) 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 + +protected: + MemRef do_alloc(const size_t size) override; + MemRef do_realloc(ref_type, 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, char*) override; + char* do_translate(ref_type) const noexcept override; + + /// Returns the first section boundary *above* the given position. + size_t get_upper_section_boundary(size_t start_pos) const noexcept; + + /// Returns the section boundary at or above the given size + size_t align_size_to_section_boundary(size_t size) 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; + + /// 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; + +private: + 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. Slabs are stored as rows in the + // Slabs table in order of ascending file offsets. + struct Slab { + ref_type ref_end; + char* addr; + size_t size; + + Slab(ref_type r, size_t s); + ~Slab(); + + Slab(const Slab&) = delete; + Slab(Slab&& other) noexcept + : ref_end(other.ref_end) + , size(other.size) + { + addr = other.addr; + other.addr = nullptr; + other.size = 0; + } + }; + + // free blocks that are in the slab area are managed using the following structures: + // - FreeBlock: Placed at the start of any free space. Holds the 'ref' corresponding to + // the start of the space, and prev/next links used to place it in a size-specific + // freelist. + // - BetweenBlocks: Structure sitting between any two free OR allocated spaces. + // describes the size of the space before and after. + // Each slab (area obtained from the underlying system) has a terminating BetweenBlocks + // at the beginning and at the end of the Slab. + struct FreeBlock { + ref_type ref; // ref for this entry. Saves a reverse translate / representing links as refs + FreeBlock* prev; // circular doubly linked list + FreeBlock* next; + void clear_links() + { + prev = next = nullptr; + } + void unlink(); + }; + struct BetweenBlocks { // stores sizes and used/free status of blocks before and after. + int32_t block_before_size; // negated if block is in use, + int32_t block_after_size; // positive if block is free - and zero at end + }; + + Config m_cfg; + using FreeListMap = std::map; // log(N) addressing for larger blocks + FreeListMap m_block_map; + + // abstract notion of a freelist - used to hide whether a freelist + // is residing in the small blocks or the large blocks structures. + struct FreeList { + int size = 0; // size of every element in the list, 0 if not found + FreeListMap::iterator it; + bool found_something() + { + return size != 0; + } + bool found_exact(int sz) + { + return size == sz; + } + }; + + // simple helper functions for accessing/navigating blocks and betweenblocks (TM) + BetweenBlocks* bb_before(FreeBlock* entry) const + { + return reinterpret_cast(entry) - 1; + } + BetweenBlocks* bb_after(FreeBlock* entry) const + { + auto bb = bb_before(entry); + size_t sz = bb->block_after_size; + char* addr = reinterpret_cast(entry) + sz; + return reinterpret_cast(addr); + } + FreeBlock* block_before(BetweenBlocks* bb) const + { + size_t sz = bb->block_before_size; + if (sz <= 0) + return nullptr; // only blocks that are not in use + char* addr = reinterpret_cast(bb) - sz; + return reinterpret_cast(addr); + } + FreeBlock* block_after(BetweenBlocks* bb) const + { + if (bb->block_after_size <= 0) + return nullptr; + return reinterpret_cast(bb + 1); + } + int size_from_block(FreeBlock* entry) const + { + return bb_before(entry)->block_after_size; + } + void mark_allocated(FreeBlock* entry); + // mark the entry freed in bordering BetweenBlocks. Also validate size. + void mark_freed(FreeBlock* entry, int size); + + // hook for the memory verifier in Group. + template + void for_all_free_entries(Func f) const; + + // Main entry points for alloc/free: + FreeBlock* allocate_block(int size); + void free_block(ref_type ref, FreeBlock* addr); + + // Searching/manipulating freelists + FreeList find(int size); + FreeList find_larger(FreeList hint, int size); + FreeBlock* pop_freelist_entry(FreeList list); + void push_freelist_entry(FreeBlock* entry); + void remove_freelist_entry(FreeBlock* element); + void rebuild_freelists_from_slab(); + void clear_freelists(); + + // grow the slab area. + // returns a free block large enough to handle the request. + FreeBlock* grow_slab(int size); + // create a single free chunk with "BetweenBlocks" at both ends and a + // single free chunk between them. This free chunk will be of size: + // slab_size - 2 * sizeof(BetweenBlocks) + FreeBlock* slab_to_entry(const Slab& slab, ref_type ref_start); + + // breaking/merging of blocks + FreeBlock* get_prev_block_if_mergeable(FreeBlock* block); + FreeBlock* get_next_block_if_mergeable(FreeBlock* block); + // break 'block' to give it 'new_size'. Return remaining block. + // If the block is too small to split, return nullptr. + FreeBlock* break_block(FreeBlock* block, int new_size); + FreeBlock* merge_blocks(FreeBlock* first, FreeBlock* second); + + // 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; + }; + + // Description of to-be-deleted memory mapping + struct OldMapping { + OldMapping(uint64_t version, util::File::Map&& map) noexcept + : replaced_at_version(version) + , mapping(std::move(map)) + { + } + OldMapping(OldMapping&& other) noexcept + : replaced_at_version(other.replaced_at_version) + , mapping() + { + mapping = std::move(other.mapping); + } + void operator=(OldMapping&& other) noexcept + { + replaced_at_version = other.replaced_at_version; + mapping = std::move(other.mapping); + } + uint64_t replaced_at_version; + util::File::Map mapping; + }; + struct OldRefTranslation { + OldRefTranslation(uint64_t v, RefTranslation* m) noexcept + { + replaced_at_version = v; + translations = m; + } + uint64_t replaced_at_version; + RefTranslation* translations; + }; + 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; + + util::RaceDetector changes; + + // mappings used by newest transactions - additional mappings may be open + // and in use by older transactions. These translations are in m_old_mappings. + std::vector> m_mappings; + // The section nr for the first mapping in m_mappings. Will be 0 for newer file formats, + // but will be nonzero if a compatibility mapping is in use. In that case, the ref for + // the first mapping is the *last* section boundary in the file. Note: in this + // mode, the first mapping in m_mappings may overlap with the last part of the + // file, leading to aliasing. + int m_sections_in_compatibility_mapping = 0; + // if the file has an older format, it needs to be mapped by a single + // mapping. This is the compatibility mapping. As such files extend, additional + // mappings are added to m_mappings (above) - the compatibility mapping remains + // unchanged until the file is closed. + // Note: If the initial file is smaller than a single section, the compatibility + // mapping is not needed and not used. Hence, it is not possible for the first mapping + // in m_mappings to completely overlap the compatibility mapping. Hence, we do not + // need special logic to detect if the compatibility mapping can be unmapped. + util::File::Map m_compatibility_mapping; + + size_t m_translation_table_size = 0; + uint64_t m_mapping_version = 1; + uint64_t m_youngest_live_version = 1; + std::mutex m_mapping_mutex; + util::File m_file; + util::SharedFileInfo* m_realm_file_info = nullptr; + // vectors where old mappings, are held from deletion to ensure translations are + // kept open and ref->ptr translations work for other threads.. + std::vector m_old_mappings; + std::vector m_old_translations; + // Rebuild the ref translations in a thread-safe manner. Save the old one along with it's + // versioning information for later deletion - 'requires_new_fast_mapping' must be + // true if there are changes to entries among the existing translations. Must be called + // with m_mapping_mutex locked. + void rebuild_translations(bool requires_new_fast_mapping, size_t old_num_sections); + // Add a translation covering a new section in the slab area. The translation is always + // added at the end. + void extend_fast_mapping_with_slab(char* address); + // Prepare the initial mapping for a file which requires use of the compatibility mapping + void setup_compatibility_mapping(size_t file_size); + + const char* m_data = nullptr; + size_t m_initial_section_size = 0; + int m_section_shifts = 0; + AttachMode m_attach_mode = attach_None; + enum FeeeSpaceState { + free_space_Clean, + free_space_Dirty, + free_space_Invalid, + }; + constexpr static int minimal_alloc = 128 * 1024; + constexpr static int maximal_alloc = 1 << section_shift; + + /// 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; + using Chunks = std::map; + Slabs m_slabs; + Chunks m_free_read_only; + size_t m_commit_size = 0; + + bool m_debug_out = false; + + /// Throws if free-lists are no longer valid. + size_t 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_header(const char* data, size_t len, const std::string& path); + void throw_header_exception(std::string msg, const Header& header, const std::string& path); + + static bool is_file_on_streaming_form(const Header& header); + /// Read the top_ref from the given buffer and set m_file_on_streaming_form + /// if the buffer contains a file in streaming form + static ref_type get_top_ref(const char* data, size_t len); + + // Gets the path of the attached file, or other relevant debugging info. + std::string get_file_path_for_assertions() const; + + static bool ref_less_than_slab_ref_end(ref_type, const Slab&) noexcept; + + friend class Group; + friend class DB; + friend class GroupWriter; +}; + + +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); + 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.load(std::memory_order_relaxed); +} + +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::align_size_to_section_boundary(size_t size) const noexcept +{ + if (matches_section_boundary(size)) + return size; + else + return get_upper_section_boundary(size); +} + +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 +{ + auto boundary = get_lower_section_boundary(pos); + return pos == boundary; +} + +template +void SlabAlloc::for_all_free_entries(Func f) const +{ + ref_type ref = align_size_to_section_boundary(m_baseline.load(std::memory_order_relaxed)); + for (const auto& e : m_slabs) { + BetweenBlocks* bb = reinterpret_cast(e.addr); + REALM_ASSERT(bb->block_before_size == 0); + while (1) { + int size = bb->block_after_size; + f(ref, sizeof(BetweenBlocks)); + ref += sizeof(BetweenBlocks); + if (size == 0) { + break; + } + if (size > 0) { // freeblock. + f(ref, size); + bb = reinterpret_cast(reinterpret_cast(bb) + sizeof(BetweenBlocks) + size); + ref += size; + } + else { + bb = reinterpret_cast(reinterpret_cast(bb) + sizeof(BetweenBlocks) - size); + ref -= size; + } + } + // any gaps in ref-space is reported as a free block to the validator: + auto next_ref = align_size_to_section_boundary(ref); + if (next_ref > ref) { + f(ref, next_ref - ref); + ref = next_ref; + } + } +} + +} // namespace realm + +#endif // REALM_ALLOC_SLAB_HPP diff --git a/src/vendor-include/realm-ios/include/realm/array.hpp b/src/vendor-include/realm-ios/include/realm/array.hpp new file mode 100644 index 000000000..f8054b1f8 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/array.hpp @@ -0,0 +1,2495 @@ +/************************************************************************* + * + * 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 +#include // size_t +#include +#include +#include +#include + +#include // unint8_t etc + +#include +#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 { + +template +inline T no0(T v) +{ + return v == 0 ? 1 : v; +} + +// Pre-definitions +struct ObjKey; +class Array; +class GroupWriter; +namespace _impl { +class ArrayWriterBase; +} + +template +class BPlusTree; + +using KeyColumn = BPlusTree; + + +struct MemStats { + size_t allocated = 0; + size_t used = 0; + size_t array_count = 0; +}; + +#ifdef REALM_DEBUG +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; +}; + + +struct TreeInsertBase { + size_t m_split_offset; + size_t m_split_size; +}; + +class Array : public Node, 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& allocator) noexcept + : Node(allocator) + { + } + + ~Array() noexcept override + { + } + + /// 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 ref) noexcept + { + REALM_ASSERT_DEBUG(ref); + char* header = m_alloc.translate(ref); + init_from_mem(MemRef(header, ref, m_alloc)); + } + + /// 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 + { + ref_type ref = get_ref_from_parent(); + init_from_ref(ref); + } + + /// 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&); + + Type get_type() const noexcept; + + + static void add_to_column(IntegerColumn* column, int64_t value); + static void add_to_column(KeyColumn* column, int64_t value); + + void insert(size_t ndx, int_fast64_t value); + void add(int_fast64_t value); + + // Used from ArrayBlob + size_t blob_size() const noexcept; + ref_type blob_replace(size_t begin, size_t end, const char* data, size_t data_size, bool add_zero_term); + + /// 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); + + //@{ + /// This is similar in spirit to std::move() from ``. + /// \a dest_begin must not be in the range [`begin`,`end`) + /// + /// This function is 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); + //@} + + // Move elements from ndx and above to another array + void move(Array& dst, size_t ndx); + + //@{ + /// 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; + + 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; + + /// 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_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; + + // Clone deep + static MemRef clone(MemRef, Allocator& from_alloc, Allocator& target_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; + + /// 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; + + static RefOrTagged get_as_ref_or_tagged(const char* header, size_t ndx) noexcept + { + return get(header, ndx); + } + + /// 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); + + 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 noexcept; + + void verify() const; + +#ifdef REALM_DEBUG + void print() const; + typedef size_t (*LeafVerifier)(MemRef, Allocator&); + void verify_bptree(LeafVerifier) 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 + + Array& operator=(const Array&) = delete; // not allowed + Array(const Array&) = delete; // not allowed +protected: + typedef bool (*CallbackDummy)(int64_t); + +protected: + // 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; + +private: + 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: + /// 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&); + + // 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; + +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); + + void report_memory_usage_2(MemUsageHandler&) const; + +private: + Getter m_getter = nullptr; // cached to avoid indirection + const VTable* m_vtable = nullptr; + +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 + + 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; + + friend class Allocator; + friend class SlabAlloc; + friend class GroupWriter; +}; + +class ClusterKeyArray : public ArrayUnsigned { +public: + using ArrayUnsigned::ArrayUnsigned; + + uint64_t get(size_t ndx) const + { + return (m_data != nullptr) ? ArrayUnsigned::get(ndx) : uint64_t(ndx); + } +}; + +// Implementation: +template <> +class QueryState : public QueryStateBase { +public: + int64_t m_state = 0; + + template + bool uses_val() + { + if (action == act_Max || action == act_Min || action == act_Sum) + return true; + else + return false; + } + + QueryState(Action action, size_t limit = -1) + : QueryState(action, int64_t(0), limit) + { + } + + QueryState(Action action, KeyColumn* akku, size_t limit = -1) + : QueryState(action, reinterpret_cast(akku), limit) + { + } + QueryState(Action action, IntegerColumn* akku, size_t limit = -1) + : QueryState(action, reinterpret_cast(akku), limit) + { + } + + 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 = m_key_values ? m_key_values->get(index) + m_key_offset : index; + } + } + else if (action == act_Min) { + if (value < m_state) { + m_state = value; + m_minmax_index = m_key_values ? m_key_values->get(index) + m_key_offset : 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) { + if (m_key_values) { + int64_t key_value = m_key_values->get(index) + m_key_offset; + Array::add_to_column(reinterpret_cast(m_state), key_value); + } + else { + 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) { + if (m_key_values) { + int64_t key_value = m_key_values->get(index) + m_key_offset; + Array::add_to_column(reinterpret_cast(m_state), key_value); + } + else { + 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; + } + +private: + QueryState(Action action, int64_t akku, size_t limit) + : QueryStateBase(limit) + { + if (action == act_Max) + m_state = std::numeric_limits::min(); + else if (action == act_Min) + m_state = std::numeric_limits::max(); + 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 = akku; + else if (action == act_CallbackIdx) { + } + else { + REALM_ASSERT_DEBUG(false); + } + } +}; + +// Used only for Basic-types: currently float and double +template +class QueryState : public QueryStateBase { +public: + R m_state; + + template + bool uses_val() + { + return (action == act_Max || action == act_Min || action == act_Sum || action == act_Count); + } + + QueryState(Action action, Array* = nullptr, size_t limit = -1) + : QueryStateBase(limit) + { + REALM_ASSERT((std::is_same::value || std::is_same::value)); + 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 if (action == act_Count) + 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; + if (m_key_values) { + m_minmax_index = m_key_values->get(index) + m_key_offset; + } + else { + m_minmax_index = int64_t(index); + } + } + } + else if (action == act_Min) { + if (value < m_state) { + m_state = value; + if (m_key_values) { + m_minmax_index = m_key_values->get(index) + m_key_offset; + } + else { + m_minmax_index = int64_t(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 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 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_hasrefs_in_header(value, get_header()); + } +} + +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_context_flag_in_header(value, get_header()); + } +} + +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_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 size_t Array::get_byte_size() const noexcept +{ + const char* header = get_header_from_data(m_data); + WidthType wtype = Node::get_wtype_from_header(header); + size_t num_bytes = NodeHeader::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 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 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_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 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) { + if (std::is_same::value) { + // In case of Equal it is safe to use the optimized logic. We just have to fetch the null value + // if this is what we are looking for. And we have to adjust the indexes to compensate for the + // null value at position 0. + if (find_null) { + value = get(0); + } + else { + // If the value to search for is equal to the null value, the value cannot be in the array + if (value == get(0)) { + return true; + } + } + start2++; + end++; + baseindex--; + } + else { + // 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. + auto null_value = get(0); + for (; start2 < end; start2++) { + int64_t v = get(start2 + 1); + bool value_is_null = (v == null_value); + if (c(v, value, value_is_null, find_null)) { + util::Optional v2(value_is_null ? 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 + a), 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 + // and the two arrays are aligned the same way + if ((reinterpret_cast(m_data) & 0xf) == (reinterpret_cast(foreign_m_data) & 0xf)) { + 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); + // todo, would be nice to avoid this in order to speed up find_first loops + QueryState state(act_ReturnFirst, 1); + 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/src/vendor-include/realm-ios/include/realm/array_backlink.hpp b/src/vendor-include/realm-ios/include/realm/array_backlink.hpp new file mode 100644 index 000000000..2ecc8c2ee --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/array_backlink.hpp @@ -0,0 +1,86 @@ +/************************************************************************* + * + * 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_BACKLINK_HPP +#define REALM_ARRAY_BACKLINK_HPP + +#include + +namespace realm { +class ArrayBacklink : public ArrayPayload, private Array { +public: + using Array::Array; + using Array::init_from_parent; + using Array::copy_on_write; + using Array::update_parent; + using Array::get_ref; + using Array::size; + + static int64_t default_value(bool) + { + return 0; + } + + void init_from_ref(ref_type ref) noexcept override + { + Array::init_from_ref(ref); + } + void set_parent(ArrayParent* parent, size_t ndx_in_parent) noexcept override + { + Array::set_parent(parent, ndx_in_parent); + } + void create() + { + Array::create(type_HasRefs); + } + + void insert(size_t ndx, int64_t val) + { + Array::insert(ndx, val); + } + + int64_t get(size_t ndx) const + { + return Array::get(ndx); + } + + void add(int64_t val) + { + Array::add(val); + } + + // nullify forward links corresponding to any backward links at index 'ndx' + void nullify_fwd_links(size_t ndx, CascadeState& state); + void add(size_t ndx, ObjKey key); + bool remove(size_t ndx, ObjKey key); + void erase(size_t ndx); + size_t get_backlink_count(size_t ndx) const; + ObjKey get_backlink(size_t ndx, size_t index) const; + void move(ArrayBacklink& dst, size_t ndx) + { + Array::move(dst, ndx); + } + void clear() + { + Array::truncate_and_destroy_children(0); + } + void verify() const; +}; +} + +#endif /* SRC_REALM_ARRAY_KEY_HPP_ */ diff --git a/src/vendor-include/realm-ios/include/realm/array_basic.hpp b/src/vendor-include/realm-ios/include/realm/array_basic.hpp new file mode 100644 index 000000000..3e231b4d4 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/array_basic.hpp @@ -0,0 +1,215 @@ +/************************************************************************* + * + * 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 ArrayPayload { +public: + using value_type = T; + + explicit BasicArray(Allocator&) noexcept; + ~BasicArray() noexcept override + { + } + + static T default_value(bool) + { + return T(0.0); + } + + void init_from_ref(ref_type ref) noexcept override + { + Array::init_from_ref(ref); + } + + void set_parent(ArrayParent* parent, size_t ndx_in_parent) noexcept override + { + Array::set_parent(parent, ndx_in_parent); + } + + // 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 + { + // 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 = BasicArray::get(ndx); + return null::is_null_float(x); + } + void add(T value); + void set(size_t ndx, T value); + void insert(size_t ndx, T value); + void erase(size_t ndx); + void truncate(size_t size); + void move(BasicArray& dst, size_t ndx) + { + for (size_t i = ndx; i < m_size; i++) { + dst.add(get(i)); + } + truncate(ndx); + } + 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; + + 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); + +#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); +}; + +template +class BasicArrayNull : public BasicArray { +public: + using BasicArray::BasicArray; + + static T default_value(bool nullable) + { + return nullable ? null::get_null_float() : T(0.0); + } + void set(size_t ndx, util::Optional value) + { + if (value) { + BasicArray::set(ndx, *value); + } + else { + BasicArray::set(ndx, null::get_null_float()); + } + } + void add(util::Optional value) + { + if (value) { + BasicArray::add(*value); + } + else { + BasicArray::add(null::get_null_float()); + } + } + void insert(size_t ndx, util::Optional value) + { + if (value) { + BasicArray::insert(ndx, *value); + } + else { + BasicArray::insert(ndx, null::get_null_float()); + } + } + + void set_null(size_t ndx) + { + // FIXME: This assumes BasicArray will only ever be instantiated for float-like T. + set(ndx, null::get_null_float()); + } + + util::Optional get(size_t ndx) const noexcept + { + T val = BasicArray::get(ndx); + return null::is_null_float(val) ? util::none : util::make_optional(val); + } + size_t find_first(util::Optional value, size_t begin = 0, size_t end = npos) const + { + if (value) { + return BasicArray::find_first(*value, begin, end); + } + else { + return find_first_null(begin, end); + } + } + void find_all(IntegerColumn* result, util::Optional value, size_t add_offset = 0, size_t begin = 0, + size_t end = npos) const + { + if (value) { + return BasicArray::find_all(result, *value, add_offset, begin, end); + } + else { + return find_all_null(result, add_offset, begin, end); + } + } + size_t find_first_null(size_t begin = 0, size_t end = npos) const; + void find_all_null(IntegerColumn* result, size_t add_offset = 0, size_t begin = 0, size_t end = npos) const; +}; + + +// Class typedefs for BasicArray's: ArrayFloat and ArrayDouble +typedef BasicArray ArrayFloat; +typedef BasicArray ArrayDouble; +typedef BasicArrayNull ArrayFloatNull; +typedef BasicArrayNull ArrayDoubleNull; + +} // namespace realm + +#include + +#endif // REALM_ARRAY_BASIC_HPP diff --git a/src/vendor-include/realm-ios/include/realm/array_basic_tpl.hpp b/src/vendor-include/realm-ios/include/realm/array_basic_tpl.hpp new file mode 100644 index 000000000..d2275d5aa --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/array_basic_tpl.hpp @@ -0,0 +1,416 @@ +/************************************************************************* + * + * 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) { + // GCC 7.x emits a false-positive strict aliasing warning for this code. Suppress it, since it + // clutters up the build output. See for details. + REALM_DIAG_PUSH(); + REALM_DIAG(ignored "-Wstrict-aliasing"); + + 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; + } + + REALM_DIAG_POP(); + } + 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 +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 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 +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 BasicArrayNull::find_first_null(size_t begin, size_t end) const +{ + size_t sz = Array::size(); + if (end == npos) + end = sz; + REALM_ASSERT(begin <= sz && end <= sz && begin <= end); + while (begin != end) { + if (this->is_null(begin)) + return begin; + begin++; + } + return not_found; +} + +template +void BasicArrayNull::find_all_null(IntegerColumn* result, size_t add_offset, size_t begin, size_t end) const +{ + size_t first = begin - 1; + for (;;) { + first = this->find_first_null(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 +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 util::overflow_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/src/vendor-include/realm-ios/include/realm/array_binary.hpp b/src/vendor-include/realm-ios/include/realm/array_binary.hpp new file mode 100644 index 000000000..d7e10ff66 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/array_binary.hpp @@ -0,0 +1,117 @@ +/************************************************************************* + * + * 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 + +namespace realm { + +class ArrayBinary : public ArrayPayload { +public: + using value_type = BinaryData; + + explicit ArrayBinary(Allocator&); + + static BinaryData default_value(bool nullable) + { + return nullable ? BinaryData{} : BinaryData{"", 0}; + } + + void create(); + + ref_type get_ref() const + { + return m_arr->get_ref(); + } + + void set_parent(ArrayParent* parent, size_t ndx_in_parent) noexcept override + { + m_arr->set_parent(parent, ndx_in_parent); + } + + void update_parent() + { + m_arr->update_parent(); + } + + void init_from_mem(MemRef mem) noexcept; + void init_from_ref(ref_type ref) noexcept override + { + init_from_mem(MemRef(m_alloc.translate(ref), ref, m_alloc)); + } + void init_from_parent(); + + size_t size() const; + + void add(BinaryData value); + void set(size_t ndx, BinaryData value); + void set_null(size_t ndx) + { + set(ndx, BinaryData{}); + } + void insert(size_t ndx, BinaryData value); + BinaryData get(size_t ndx) const; + BinaryData get_at(size_t ndx, size_t& pos) const; + bool is_null(size_t ndx) const; + void erase(size_t ndx); + void move(ArrayBinary& dst, size_t ndx); + void clear(); + + size_t find_first(BinaryData value, size_t begin, size_t end) 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 BinaryData get(const char* header, size_t ndx, Allocator& alloc) noexcept; + + void verify() const; + +private: + static constexpr size_t small_blob_max_size = 64; + + union Storage { + std::aligned_storage::type m_small_blobs; + std::aligned_storage::type m_big_blobs; + }; + + bool m_is_big = false; + + Allocator& m_alloc; + Storage m_storage; + Array* m_arr; + + bool upgrade_leaf(size_t value_size); +}; + +inline BinaryData ArrayBinary::get(const char* header, size_t ndx, Allocator& alloc) noexcept +{ + bool is_big = Array::get_context_flag_from_header(header); + if (!is_big) { + return ArraySmallBlobs::get(header, ndx, alloc); + } + else { + return ArrayBigBlobs::get(header, ndx, alloc); + } +} +} + +#endif /* SRC_REALM_ARRAY_BINARY_HPP_ */ diff --git a/src/vendor-include/realm-ios/include/realm/array_blob.hpp b/src/vendor-include/realm-ios/include/realm/array_blob.hpp new file mode 100644 index 000000000..556d40a3b --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/array_blob.hpp @@ -0,0 +1,146 @@ +/************************************************************************* + * + * 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&); + +#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/src/vendor-include/realm-ios/include/realm/array_blobs_big.hpp b/src/vendor-include/realm-ios/include/realm/array_blobs_big.hpp new file mode 100644 index 000000000..beb2e66df --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/array_blobs_big.hpp @@ -0,0 +1,209 @@ +/************************************************************************* + * + * 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; + bool is_null(size_t ndx) const; + 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; + + //@{ + /// 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; + //@} + + /// 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(); + +#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 sz = get_size_from_header(blob_header); + return BinaryData(value, sz); + } + return {}; +} + +inline bool ArrayBigBlobs::is_null(size_t ndx) const +{ + ref_type ref = get_as_ref(ndx); + return ref == 0; +} + +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 sz = Array::get_size_from_header(blob_header); + return BinaryData(blob_data, sz); + } + 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 = true) 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 void ArrayBigBlobs::create() +{ + bool context_flag = true; + Array::create(type_HasRefs, context_flag); // Throws +} + +} // namespace realm + +#endif // REALM_ARRAY_BIG_BLOBS_HPP diff --git a/src/vendor-include/realm-ios/include/realm/array_blobs_small.hpp b/src/vendor-include/realm-ios/include/realm/array_blobs_small.hpp new file mode 100644 index 000000000..04b56a46c --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/array_blobs_small.hpp @@ -0,0 +1,281 @@ +/************************************************************************* + * + * 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_BLOBS_SMALL_HPP +#define REALM_ARRAY_BLOBS_SMALL_HPP + +#include +#include +#include +#include + +namespace realm { + +/* +STORAGE FORMAT +--------------------------------------------------------------------------------------- +ArraySmallBlobs 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 ArraySmallBlobs 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 ArraySmallBlobs : public Array { +public: + explicit ArraySmallBlobs(Allocator&) noexcept; + ~ArraySmallBlobs() noexcept override + { + } + + // Disable copying, this is not allowed. + ArraySmallBlobs& operator=(const ArraySmallBlobs&) = delete; + ArraySmallBlobs(const ArraySmallBlobs&) = 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(); + + //@{ + /// 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; + StringData get_string(size_t ndx) const; + bool is_null(size_t ndx) const; + + 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 add_string(StringData value); + void set_string(size_t ndx, StringData value); + void insert_string(size_t ndx, StringData value); + void erase(size_t ndx); + void truncate(size_t new_size); + void clear(); + void destroy(); + + size_t find_first(BinaryData value, bool is_string, size_t begin, size_t end) 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 BinaryData get(const char* header, size_t ndx, Allocator&) noexcept; + static StringData get_string(const char* header, size_t ndx, Allocator& alloc) noexcept; + + 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); + +#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: + friend class ArrayString; + ArrayInteger m_offsets; + ArrayBlob m_blob; + Array m_nulls; + + StringData get_string_legacy(size_t ndx) const; +}; + + +// Implementation: + +inline ArraySmallBlobs::ArraySmallBlobs(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 ArraySmallBlobs::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 ArraySmallBlobs::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 ArraySmallBlobs::init_from_parent() noexcept +{ + ref_type ref = get_ref_from_parent(); + init_from_ref(ref); +} + +inline bool ArraySmallBlobs::is_empty() const noexcept +{ + return m_offsets.is_empty(); +} + +inline size_t ArraySmallBlobs::size() const noexcept +{ + return m_offsets.size(); +} + +inline BinaryData ArraySmallBlobs::get(size_t ndx) const noexcept +{ + REALM_ASSERT_3(ndx, <, m_offsets.size()); + + if (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 bool ArraySmallBlobs::is_null(size_t ndx) const +{ + REALM_ASSERT_3(ndx, <, m_nulls.size()); + + return m_nulls.get(ndx) != 0; +} + +inline StringData ArraySmallBlobs::get_string(size_t ndx) const +{ + 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 StringData ArraySmallBlobs::get_string(const char* header, size_t ndx, Allocator& alloc) noexcept +{ + BinaryData bin = get(header, ndx, alloc); + if (bin.is_null()) + return realm::null(); + else + return StringData(bin.data(), bin.size() - 1); // Do not include terminating zero +} + +inline void ArraySmallBlobs::add_string(StringData value) +{ + add(BinaryData(value.data(), value.size()), true); +} + +inline void ArraySmallBlobs::set_string(size_t ndx, StringData value) +{ + set(ndx, BinaryData(value.data(), value.size()), true); +} + +inline void ArraySmallBlobs::insert_string(size_t ndx, StringData value) +{ + insert(ndx, BinaryData(value.data(), value.size()), true); +} + +inline void ArraySmallBlobs::truncate(size_t new_size) +{ + REALM_ASSERT(new_size == 0 || new_size < m_offsets.size()); + + size_t sz = new_size ? to_size_t(m_offsets.get(new_size - 1)) : 0; + + m_offsets.truncate(new_size); + m_blob.truncate(sz); + m_nulls.truncate(new_size); +} + +inline void ArraySmallBlobs::clear() +{ + m_blob.clear(); + m_offsets.clear(); + m_nulls.clear(); +} + +inline void ArraySmallBlobs::destroy() +{ + m_blob.destroy(); + m_offsets.destroy(); + m_nulls.destroy(); + Array::destroy(); +} + +inline size_t ArraySmallBlobs::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 ArraySmallBlobs::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); + m_nulls.update_from_parent(old_baseline); + } + return res; +} + +} // namespace realm + +#endif // REALM_ARRAY_BINARY_HPP diff --git a/src/vendor-include/realm-ios/include/realm/array_bool.hpp b/src/vendor-include/realm-ios/include/realm/array_bool.hpp new file mode 100644 index 000000000..6b2c402be --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/array_bool.hpp @@ -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. + * + **************************************************************************/ + +#ifndef REALM_ARRAY_BOOL_HPP +#define REALM_ARRAY_BOOL_HPP + +#include + +namespace realm { + +/// ArrayBool supports both nullable and non-nullable arrays with respect to +/// adding and inserting values. In this way we don't need to distinguish +/// between the two type when adding a row and when adding a column. +/// add, insert and getting of non-nullable values are taken care of by the +/// respective functions in Array. +class ArrayBool : public Array, public ArrayPayload { +public: + using value_type = bool; + + using Array::Array; + using Array::init_from_ref; + using Array::init_from_parent; + using Array::update_parent; + using Array::get_ref; + using Array::size; + using Array::erase; + using Array::truncate_and_destroy_children; + + static bool default_value(bool) + { + return false; + } + void create() + { + Array::create(type_Normal); + } + void init_from_ref(ref_type ref) noexcept override + { + Array::init_from_ref(ref); + } + void set_parent(ArrayParent* parent, size_t ndx_in_parent) noexcept override + { + Array::set_parent(parent, ndx_in_parent); + } + bool is_null(size_t) const + { + return false; + } + void set(size_t ndx, bool value) + { + Array::set(ndx, value ? 1 : 0); + } + bool get(size_t ndx) const + { + return Array::get(ndx) != 0; + } + void add(bool value) + { + Array::add(value); + } + void insert(size_t ndx, bool value) + { + Array::insert(ndx, value); + } + + size_t find_first(util::Optional value, size_t begin = 0, size_t end = npos) const noexcept + { + if (value) { + return Array::find_first(*value, begin, end); + } + else { + return Array::find_first(null_value, begin, end); + } + } + +protected: + // We can still be in two bits as small values are considered unsigned + static constexpr int null_value = 3; +}; + +class ArrayBoolNull : public ArrayBool { +public: + using value_type = util::Optional; + using ArrayBool::ArrayBool; + + static util::Optional default_value(bool nullable) + { + return nullable ? util::none : util::some(false); + } + void set(size_t ndx, util::Optional value) + { + if (value) { + Array::set(ndx, *value); + } + else { + Array::set(ndx, null_value); + } + } + void add(util::Optional value) + { + if (value) { + Array::add(*value); + } + else { + Array::add(null_value); + } + } + void insert(size_t ndx, util::Optional value) + { + if (value) { + Array::insert(ndx, *value); + } + else { + Array::insert(ndx, null_value); + } + } + void set_null(size_t ndx) + { + Array::set(ndx, null_value); + } + bool is_null(size_t ndx) const + { + return Array::get(ndx) == null_value; + } + util::Optional get(size_t ndx) const + { + int64_t val = Array::get(ndx); + return (val == null_value) ? util::none : util::make_optional(val != 0); + } +}; +} + +#endif /* REALM_ARRAY_BOOL_HPP */ diff --git a/src/vendor-include/realm-ios/include/realm/array_direct.hpp b/src/vendor-include/realm-ios/include/realm/array_direct.hpp new file mode 100644 index 000000000..18278ae87 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/array_direct.hpp @@ -0,0 +1,382 @@ +/************************************************************************* + * + * 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 { + +/// 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} +size_t bit_width(int64_t value); + +// 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); + } +} + +inline void set_direct(char* data, size_t width, size_t ndx, int_fast64_t value) noexcept +{ + REALM_TEMPEX(set_direct, width, (data, ndx, value)); +} + +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/src/vendor-include/realm-ios/include/realm/array_integer.hpp b/src/vendor-include/realm-ios/include/realm/array_integer.hpp new file mode 100644 index 000000000..2d013550b --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/array_integer.hpp @@ -0,0 +1,674 @@ +/************************************************************************* + * + * 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 ArrayPayload { +public: + using value_type = int64_t; + + explicit ArrayInteger(Allocator&) noexcept; + ~ArrayInteger() noexcept override + { + } + + static value_type default_value(bool) + { + return 0; + } + + void init_from_ref(ref_type ref) noexcept override + { + Array::init_from_ref(ref); + } + void set_parent(ArrayParent* parent, size_t ndx_in_parent) noexcept override + { + Array::set_parent(parent, ndx_in_parent); + } + + // 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; + void move(ArrayInteger& dst, size_t ndx) + { + Array::move(dst, ndx); + } + + bool is_null(size_t) const + { + return false; + } + + /// 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; + +private: + template + bool minmax(size_t from, size_t to, uint64_t maxdiff, int64_t* min, int64_t* max) const; +}; + +class ArrayRef : public ArrayInteger { +public: + using value_type = ref_type; + + explicit ArrayRef(Allocator& allocator) noexcept + : ArrayInteger(allocator) + { + } + + void create(size_t sz = 0) + { + Array::create(type_HasRefs, false, sz, 0); + } + + void add(ref_type value) + { + Array::add(from_ref(value)); + } + + void set(size_t ndx, ref_type value) + { + Array::set(ndx, from_ref(value)); + } + + void insert(size_t ndx, ref_type value) + { + Array::insert(ndx, from_ref(value)); + } + + ref_type get(size_t ndx) const noexcept + { + return to_ref(Array::get(ndx)); + } + void verify() const; +}; + +class ArrayIntNull : public Array, public ArrayPayload { +public: + using value_type = util::Optional; + + explicit ArrayIntNull(Allocator&) noexcept; + ~ArrayIntNull() noexcept override; + + static value_type default_value(bool nullable) + { + return nullable ? util::none : util::Optional(0); + } + + /// 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, Allocator&); + void create(Type = type_Normal, bool context_flag = false); + + void init_from_ref(ref_type) noexcept override; + void set_parent(ArrayParent* parent, size_t ndx_in_parent) noexcept override + { + Array::set_parent(parent, ndx_in_parent); + } + 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); + 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); + 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 move(ArrayIntNull& dst, size_t ndx); + void clear(); + void set_all_to_zero(); + + void move(size_t begin, size_t end, size_t dest_begin); + + 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; + +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, 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) +{ + 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) +{ + 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::clear() +{ + Array::truncate(0); + Array::add(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 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(act_ReturnFirst, 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/src/vendor-include/realm-ios/include/realm/array_key.hpp b/src/vendor-include/realm-ios/include/realm/array_key.hpp new file mode 100644 index 000000000..0a6a1f12c --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/array_key.hpp @@ -0,0 +1,127 @@ +/************************************************************************* + * + * 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_KEY_HPP +#define REALM_ARRAY_KEY_HPP + +#include +#include +#include + +namespace realm { + +// If this class is used directly in a cluster leaf, the links are stored as the +// link value +1 in order to represent the null_key (-1) as 0. If the class is used +// in BPlusTree class, the values should not be adjusted. +template +class ArrayKeyBase : public ArrayPayload, private Array { +public: + using value_type = ObjKey; + + using Array::is_attached; + using Array::init_from_mem; + using Array::init_from_parent; + using Array::update_parent; + using Array::get_ref; + using Array::size; + using Array::erase; + using Array::clear; + using Array::destroy; + using Array::verify; + + ArrayKeyBase(Allocator& allocator) + : Array(allocator) + { + } + + static ObjKey default_value(bool) + { + return {}; + } + + void init_from_ref(ref_type ref) noexcept override + { + Array::init_from_ref(ref); + } + + void set_parent(ArrayParent* parent, size_t ndx_in_parent) noexcept override + { + Array::set_parent(parent, ndx_in_parent); + } + + void create() + { + Array::create(type_Normal); + } + + void add(ObjKey value) + { + Array::add(value.value + adj); + } + void set(size_t ndx, ObjKey value) + { + Array::set(ndx, value.value + adj); + } + + void set_null(size_t ndx) + { + Array::set(ndx, 0); + } + void insert(size_t ndx, ObjKey value) + { + Array::insert(ndx, value.value + adj); + } + ObjKey get(size_t ndx) const + { + return ObjKey{Array::get(ndx) - adj}; + } + bool is_null(size_t ndx) const + { + return Array::get(ndx) == 0; + } + void move(ArrayKeyBase& dst, size_t ndx) + { + Array::move(dst, ndx); + } + + size_t find_first(ObjKey value, size_t begin, size_t end) const noexcept + { + return Array::find_first(value.value + adj, begin, end); + } + + void nullify(ObjKey key) + { + size_t begin = find_first(key, 0, Array::size()); + // There must be one + REALM_ASSERT(begin != realm::npos); + Array::erase(begin); + } +}; + +class ArrayKey : public ArrayKeyBase<1> { +public: + using ArrayKeyBase::ArrayKeyBase; +}; + +class ArrayKeyNonNullable : public ArrayKeyBase<0> { +public: + using ArrayKeyBase::ArrayKeyBase; +}; +} + +#endif /* SRC_REALM_ARRAY_KEY_HPP_ */ diff --git a/src/vendor-include/realm-ios/include/realm/array_list.hpp b/src/vendor-include/realm-ios/include/realm/array_list.hpp new file mode 100644 index 000000000..fa4170064 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/array_list.hpp @@ -0,0 +1,94 @@ +/************************************************************************* + * + * 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_LIST_HPP +#define REALM_ARRAY_LIST_HPP + +#include + +namespace realm { + +class ArrayList : public ArrayPayload, private Array { +public: + using value_type = ref_type; + + using Array::Array; + using Array::init_from_parent; + using Array::update_parent; + using Array::get_ref; + using Array::size; + using Array::erase; + + static ref_type default_value(bool) + { + return 0; + } + + void init_from_ref(ref_type ref) noexcept override + { + Array::init_from_ref(ref); + } + + void set_parent(ArrayParent* parent, size_t ndx_in_parent) noexcept override + { + Array::set_parent(parent, ndx_in_parent); + } + + void create() + { + Array::create(type_HasRefs); + } + + void add(ref_type value) + { + Array::add(from_ref(value)); + } + void set(size_t ndx, ref_type value) + { + Array::set_as_ref(ndx, value); + } + + void set_null(size_t ndx) + { + Array::set(ndx, 0); + } + void insert(size_t ndx, ref_type value) + { + Array::insert(ndx, from_ref(value)); + } + ref_type get(size_t ndx) const + { + return Array::get_as_ref(ndx); + } + bool is_null(size_t ndx) const + { + return Array::get(ndx) == 0; + } + void truncate_and_destroy_children(size_t ndx) + { + Array::truncate(ndx); + } + + size_t find_first(ref_type value, size_t begin, size_t end) const noexcept + { + return Array::find_first(from_ref(value), begin, end); + } +}; +} + +#endif /* REALM_ARRAY_LIST_HPP */ diff --git a/src/vendor-include/realm-ios/include/realm/array_mixed.hpp b/src/vendor-include/realm-ios/include/realm/array_mixed.hpp new file mode 100644 index 000000000..be6adfb7c --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/array_mixed.hpp @@ -0,0 +1,130 @@ +/************************************************************************* + * + * Copyright 2018 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_MIXED_HPP +#define REALM_ARRAY_MIXED_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace realm { + +class ArrayMixed : public ArrayPayload, private Array { +public: + using value_type = Mixed; + + using Array::detach; + using Array::is_attached; + using Array::get_ref; + using Array::set_parent; + using Array::update_parent; + using Array::get_parent; + + explicit ArrayMixed(Allocator&); + + static Mixed default_value(bool) + { + return Mixed{}; + } + + void create(); + void destroy() + { + Array::destroy_deep(); + } + + void init_from_mem(MemRef mem) noexcept; + void init_from_ref(ref_type ref) noexcept override + { + init_from_mem(MemRef(m_alloc.translate(ref), ref, m_alloc)); + } + void set_parent(ArrayParent* parent, size_t ndx_in_parent) noexcept override + { + Array::set_parent(parent, ndx_in_parent); + } + void init_from_parent() + { + ref_type ref = get_ref_from_parent(); + init_from_ref(ref); + } + + size_t size() const + { + return m_composite.size(); + } + + void add(Mixed value); + void set(size_t ndx, Mixed value); + void set_null(size_t ndx); + void insert(size_t ndx, Mixed value); + Mixed get(size_t ndx) const; + bool is_null(size_t ndx) const + { + return m_composite.get(ndx) == 0; + } + + void erase(size_t ndx); + void truncate_and_destroy_children(size_t ndx); + void move(ArrayMixed& dst, size_t ndx); + + size_t find_first(Mixed value, size_t begin = 0, size_t end = realm::npos) const noexcept; + +private: + enum { payload_idx_type, payload_idx_int, payload_idx_pair, payload_idx_str, payload_idx_size }; + + static constexpr int64_t s_data_type_mask = 0b0001'1111; + static constexpr int64_t s_payload_idx_mask = 0b1110'0000; + static constexpr int64_t s_payload_idx_shift = 5; + static constexpr int64_t s_data_shift = 8; + + // This primary array contains an aggregation of the actual value - which can be + // either the value itself or an index into one of the payload arrays - the index + // of the payload array and the data_type. + // + // value << s_data_shift | payload_idx << s_payload_idx_shift | data_type + // + // payload_idx one of PayloadIdx + Array m_composite; + + // Used to store big ints, floats and doubles + mutable Array m_ints; + // Used to store timestamps + mutable Array m_int_pairs; + // Used to store String and Binary + mutable ArrayString m_strings; + + DataType get_type(size_t ndx) const + { + return DataType((m_composite.get(ndx) & s_data_type_mask) - 1); + } + int64_t store(const Mixed&); + void ensure_array_accessor(Array& arr, size_t ndx_in_parent) const; + void ensure_int_array() const; + void ensure_int_pair_array() const; + void ensure_string_array() const; + void replace_index(size_t old_ndx, size_t new_ndx, size_t payload_index); + void erase_linked_payload(size_t ndx); +}; +} // namespace realm + +#endif /* REALM_ARRAY_MIXED_HPP */ diff --git a/src/vendor-include/realm-ios/include/realm/array_string.hpp b/src/vendor-include/realm-ios/include/realm/array_string.hpp new file mode 100644 index 000000000..77459e377 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/array_string.hpp @@ -0,0 +1,190 @@ +/************************************************************************* + * + * 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 +#include +#include + +namespace realm { + +class Spec; + +class ArrayString : public ArrayPayload { +public: + using value_type = StringData; + + explicit ArrayString(Allocator&); + + static StringData default_value(bool nullable) + { + return nullable ? StringData{} : StringData{""}; + } + + void create(); + + bool is_attached() const + { + return m_arr->is_attached(); + } + + ref_type get_ref() const + { + return m_arr->get_ref(); + } + ArrayParent* get_parent() const + { + return m_arr->get_parent(); + } + size_t get_ndx_in_parent() const + { + return m_arr->get_ndx_in_parent(); + } + void set_parent(ArrayParent* p, size_t n) noexcept override + { + m_arr->set_parent(p, n); + } + bool need_spec() const override + { + return true; + } + void set_spec(Spec* spec, size_t col_ndx) const override + { + m_spec = spec; + m_col_ndx = col_ndx; + } + + void update_parent() + { + m_arr->update_parent(); + } + + void init_from_mem(MemRef mem) noexcept; + void init_from_ref(ref_type ref) noexcept override + { + init_from_mem(MemRef(m_alloc.translate(ref), ref, m_alloc)); + } + void init_from_parent(); + void destroy(); + + size_t size() const; + + void add(StringData value); + void set(size_t ndx, StringData value); + void set_null(size_t ndx) + { + set(ndx, StringData{}); + } + void insert(size_t ndx, StringData value); + StringData get(size_t ndx) const; + StringData get_legacy(size_t ndx) const; + bool is_null(size_t ndx) const; + void erase(size_t ndx); + void move(ArrayString& dst, size_t ndx); + void clear(); + + size_t find_first(StringData value, size_t begin, size_t end) const noexcept; + + size_t lower_bound(StringData value); + + /// 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& alloc) noexcept; + + void verify() const; + +private: + static constexpr size_t small_string_max_size = 15; // ArrayStringShort + static constexpr size_t medium_string_max_size = 63; // ArrayStringLong + union Storage { + std::aligned_storage::type m_string_short; + std::aligned_storage::type m_string_long; + std::aligned_storage::type m_big_blobs; + std::aligned_storage::type m_enum; + }; + enum class Type { small_strings, medium_strings, big_strings, enum_strings }; + + Type m_type = Type::small_strings; + + Allocator& m_alloc; + Storage m_storage; + Array* m_arr; + mutable Spec* m_spec = nullptr; + mutable size_t m_col_ndx = realm::npos; + + std::unique_ptr m_string_enum_values; + + Type upgrade_leaf(size_t value_size); +}; + +inline StringData ArrayString::get(const char* header, size_t ndx, Allocator& alloc) noexcept +{ + bool long_strings = Array::get_hasrefs_from_header(header); + if (!long_strings) { + return ArrayStringShort::get(header, ndx, true); + } + else { + bool is_big = Array::get_context_flag_from_header(header); + if (!is_big) { + return ArraySmallBlobs::get_string(header, ndx, alloc); + } + else { + return ArrayBigBlobs::get_string(header, ndx, alloc); + } + } +} + +template <> +class QueryState : public QueryStateBase { +public: + StringData m_state; + + template + bool uses_val() + { + return (action == act_Count); + } + + QueryState(Action, Array* = nullptr, size_t limit = -1) + : QueryStateBase(limit) + { + } + + template + inline bool match(size_t, uint64_t, StringData) + { + if (pattern) + return false; + + if (action == act_Count) { + ++m_match_count; + } + else { + REALM_ASSERT_DEBUG(false); + } + + return (m_limit > m_match_count); + } +}; +} + +#endif /* REALM_ARRAY_STRING_HPP */ diff --git a/src/vendor-include/realm-ios/include/realm/array_string_short.hpp b/src/vendor-include/realm-ios/include/realm/array_string_short.hpp new file mode 100644 index 000000000..f2c025703 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/array_string_short.hpp @@ -0,0 +1,174 @@ +/************************************************************************* + * + * 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_SHORT_HPP +#define REALM_ARRAY_STRING_SHORT_HPP + +#include + +namespace realm { + +/* +ArrayStringShort 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 ArrayStringShort : public Array { +public: + static const size_t max_width = 64; + + typedef StringData value_type; + // Constructor defaults to non-nullable because we use non-nullable ArrayStringShort so many places internally + // in core (data which isn't user payload) where null isn't needed. + explicit ArrayStringShort(Allocator&, bool nullable = false) noexcept; + ~ArrayStringShort() 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 ArrayStringShort&) 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; + + /// 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(); + +#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 ArrayStringShort::ArrayStringShort(Allocator& allocator, bool nullable) noexcept + : Array(allocator) + , m_nullable(nullable) +{ +} + +inline void ArrayStringShort::create() +{ + size_t init_size = 0; + MemRef mem = create_array(init_size, get_alloc()); // Throws + init_from_mem(mem); +} + +inline MemRef ArrayStringShort::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 ArrayStringShort::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 ArrayStringShort::add(StringData value) +{ + REALM_ASSERT(!(!m_nullable && value.is_null())); + insert(m_size, value); // Throws +} + +inline void ArrayStringShort::add() +{ + add(m_nullable ? realm::null() : StringData("")); // Throws +} + +inline StringData ArrayStringShort::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/src/vendor-include/realm-ios/include/realm/array_timestamp.hpp b/src/vendor-include/realm-ios/include/realm/array_timestamp.hpp new file mode 100644 index 000000000..56eec139d --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/array_timestamp.hpp @@ -0,0 +1,189 @@ +/************************************************************************* + * + * 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_TIMESTAMP_HPP +#define REALM_ARRAY_TIMESTAMP_HPP + +#include +#include + +namespace realm { + +class ArrayTimestamp : public ArrayPayload, private Array { +public: + using value_type = Timestamp; + + explicit ArrayTimestamp(Allocator&); + + using Array::update_parent; + using Array::get_parent; + using Array::get_ndx_in_parent; + using Array::get_ref; + + static Timestamp default_value(bool nullable) + { + return nullable ? Timestamp{} : Timestamp{0, 0}; + } + + void create(); + + void init_from_mem(MemRef mem) noexcept; + void init_from_ref(ref_type ref) noexcept override + { + init_from_mem(MemRef(m_alloc.translate(ref), ref, m_alloc)); + } + void set_parent(ArrayParent* parent, size_t ndx_in_parent) noexcept override + { + Array::set_parent(parent, ndx_in_parent); + } + void init_from_parent() + { + init_from_ref(Array::get_ref_from_parent()); + } + + size_t size() const + { + return m_seconds.size(); + } + + void add(Timestamp value) + { + insert(m_seconds.size(), value); + } + void set(size_t ndx, Timestamp value); + void set_null(size_t ndx) + { + // Value in m_nanoseconds is irrelevant if m_seconds is null + m_seconds.set_null(ndx); // Throws + } + void insert(size_t ndx, Timestamp value); + Timestamp get(size_t ndx) const + { + util::Optional seconds = m_seconds.get(ndx); + return seconds ? Timestamp(*seconds, int32_t(m_nanoseconds.get(ndx))) : Timestamp{}; + } + bool is_null(size_t ndx) const + { + return m_seconds.is_null(ndx); + } + void erase(size_t ndx) + { + m_seconds.erase(ndx); + m_nanoseconds.erase(ndx); + } + void move(ArrayTimestamp& dst, size_t ndx) + { + m_seconds.move(dst.m_seconds, ndx); + m_nanoseconds.move(dst.m_nanoseconds, ndx); + } + void clear() + { + m_seconds.clear(); + m_nanoseconds.clear(); + } + + template + size_t find_first(Timestamp value, size_t begin, size_t end) const noexcept; + + size_t find_first(Timestamp value, size_t begin, size_t end) const noexcept; + + void verify() const; + +private: + ArrayIntNull m_seconds; + ArrayInteger m_nanoseconds; +}; + +template <> +size_t ArrayTimestamp::find_first(Timestamp value, size_t begin, size_t end) const noexcept; +template <> +size_t ArrayTimestamp::find_first(Timestamp value, size_t begin, size_t end) const noexcept; +template <> +size_t ArrayTimestamp::find_first(Timestamp value, size_t begin, size_t end) const noexcept; +template <> +size_t ArrayTimestamp::find_first(Timestamp value, size_t begin, size_t end) const noexcept; +template <> +size_t ArrayTimestamp::find_first(Timestamp value, size_t begin, size_t end) const noexcept; +template <> +size_t ArrayTimestamp::find_first(Timestamp value, size_t begin, size_t end) const noexcept; + +inline size_t ArrayTimestamp::find_first(Timestamp value, size_t begin, size_t end) const noexcept +{ + return find_first(value, begin, end); +} + + +template <> +class QueryState : public QueryStateBase { +public: + Timestamp m_state; + + template + bool uses_val() + { + return (action == act_Max || action == act_Min || action == act_Sum || action == act_Count); + } + + QueryState(Action action, Array* = nullptr, size_t limit = -1) + : QueryStateBase(limit) + { + if (action == act_Max) + m_state = Timestamp{std::numeric_limits::min(), 0}; + else if (action == act_Min) + m_state = Timestamp{std::numeric_limits::max(), 0}; + else { + REALM_ASSERT_DEBUG(false); + } + } + + template + inline bool match(size_t index, uint64_t /*indexpattern*/, Timestamp value) + { + if (pattern) + return false; + + if (action == act_Count) { + ++m_match_count; + } + else { + ++m_match_count; + if (action == act_Max) { + if (value > m_state) { + m_state = value; + REALM_ASSERT(m_key_values); + m_minmax_index = m_key_values->get(index) + m_key_offset; + } + } + else if (action == act_Min) { + if (value < m_state) { + m_state = value; + REALM_ASSERT(m_key_values); + m_minmax_index = m_key_values->get(index) + m_key_offset; + } + } + else { + REALM_ASSERT_DEBUG(false); + } + } + + return (m_limit > m_match_count); + } +}; +} + +#endif /* SRC_REALM_ARRAY_BINARY_HPP_ */ diff --git a/src/vendor-include/realm-ios/include/realm/array_unsigned.hpp b/src/vendor-include/realm-ios/include/realm/array_unsigned.hpp new file mode 100644 index 000000000..25d525d27 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/array_unsigned.hpp @@ -0,0 +1,101 @@ +/************************************************************************* + * + * 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_UNSIGNED_HPP +#define REALM_ARRAY_UNSIGNED_HPP + +#include + +namespace realm { + +// Array holding unsigned values only +class ArrayUnsigned : public Node { + +public: + ArrayUnsigned(Allocator& allocator) + : Node(allocator) + { + } + + // Will create an uninitialized array of size 'initial_size' + // Has a width big enough to hold values smaller than 'ubound_value' + void create(size_t initial_size, uint64_t ubound_value); + + void 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)); + } + + bool update_from_parent(size_t old_baseline) noexcept; + + void init_from_mem(MemRef mem) noexcept + { + Node::init_from_mem(mem); + set_width(m_width); + } + + size_t lower_bound(uint64_t value) const noexcept; + size_t upper_bound(uint64_t value) const noexcept; + + void add(uint64_t value) + { + insert(m_size, value); + } + // insert value at index (moving successive elements 1 position forwards) + void insert(size_t ndx, uint64_t value); + // delete value at index + void erase(size_t ndx); + // return value at index + uint64_t get(size_t index) const; + // return tagged value at index + // override value at index + void set(size_t ndx, uint64_t value); + + void adjust(size_t ndx, int64_t diff) + { + if (diff != 0) { + set(ndx, get(ndx) + diff); // Throws + } + } + + void adjust(size_t begin, size_t end, int64_t diff) + { + if (diff != 0) { + // FIXME: Should be optimized + for (size_t i = begin; i < end; ++i) + adjust(i, diff); // Throws + } + } + + void truncate(size_t ndx); + +private: + uint64_t m_ubound; // max number that can be stored with current m_width + + void set_width(uint8_t width); + uint8_t bit_width(uint64_t value); + + void _set(size_t ndx, uint8_t width, uint64_t value); + uint64_t _get(size_t ndx, uint8_t width) const; +}; + +} // namespace + +#endif /* REALM_ARRAY_UNSIGNED_HPP */ diff --git a/src/vendor-include/realm-ios/include/realm/binary_data.hpp b/src/vendor-include/realm-ios/include/realm/binary_data.hpp new file mode 100644 index 000000000..b4a110faa --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/binary_data.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_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) +{ + if (d.is_null()) { + out << "null"; + } + else { + 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/src/vendor-include/realm-ios/include/realm/bplustree.hpp b/src/vendor-include/realm-ios/include/realm/bplustree.hpp new file mode 100644 index 000000000..3b22feb79 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/bplustree.hpp @@ -0,0 +1,727 @@ +/************************************************************************* + * + * Copyright 2018 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_BPLUSTREE_HPP +#define REALM_BPLUSTREE_HPP + +#include +#include +#include + +namespace realm { + +class BPlusTreeBase; +class BPlusTreeInner; + +/*****************************************************************************/ +/* BPlusTreeNode */ +/* Base class for all nodes in the BPlusTree. Provides an abstract interface */ +/* that can be used by the BPlusTreeBase class to manipulate the tree. */ +/*****************************************************************************/ +class BPlusTreeNode { +public: + struct State { + int64_t split_offset; + size_t split_size; + }; + + // Insert an element at 'insert_pos'. May cause node to be split + using InsertFunc = util::FunctionRef; + // Access element at 'ndx'. Insertion/deletion not allowed + using AccessFunc = util::FunctionRef; + // Erase element at erase_pos. May cause nodes to be merged + using EraseFunc = util::FunctionRef; + // Function to be called for all leaves in the tree until the function + // returns 'true'. 'offset' gives index of the first element in the leaf. + using TraverseFunc = util::FunctionRef; + + BPlusTreeNode(BPlusTreeBase* tree) + : m_tree(tree) + { + } + + void change_owner(BPlusTreeBase* tree) + { + m_tree = tree; + } + + virtual ~BPlusTreeNode(); + + virtual bool is_leaf() const = 0; + virtual bool is_compact() const = 0; + virtual ref_type get_ref() const = 0; + + virtual void init_from_ref(ref_type ref) noexcept = 0; + + virtual void bp_set_parent(ArrayParent* parent, size_t ndx_in_parent) = 0; + virtual void update_parent() = 0; + + // Number of elements in this node + virtual size_t get_node_size() const = 0; + // Size of subtree + virtual size_t get_tree_size() const = 0; + + virtual ref_type bptree_insert(size_t n, State& state, InsertFunc) = 0; + virtual void bptree_access(size_t n, AccessFunc) = 0; + virtual size_t bptree_erase(size_t n, EraseFunc) = 0; + virtual bool bptree_traverse(TraverseFunc) = 0; + + // Move elements over in new node, starting with element at position 'ndx'. + // If this is an inner node, the index offsets should be adjusted with 'adj' + virtual void move(BPlusTreeNode* new_node, size_t ndx, int64_t offset_adj) = 0; + virtual void verify() const = 0; + +protected: + BPlusTreeBase* m_tree; +}; + +/*****************************************************************************/ +/* BPlusTreeLeaf */ +/* Base class for all leaf nodes. */ +/*****************************************************************************/ +class BPlusTreeLeaf : public BPlusTreeNode { +public: + using BPlusTreeNode::BPlusTreeNode; + + bool is_leaf() const override + { + return true; + } + + bool is_compact() const override + { + return true; + } + + ref_type bptree_insert(size_t n, State& state, InsertFunc) override; + void bptree_access(size_t n, AccessFunc) override; + size_t bptree_erase(size_t n, EraseFunc) override; + bool bptree_traverse(TraverseFunc) override; + void verify() const override + { + } +}; + +/*****************************************************************************/ +/* BPlusTreeBase */ +/* Base class for the actual tree classes. */ +/*****************************************************************************/ +class BPlusTreeBase { +public: + BPlusTreeBase(Allocator& alloc) + : m_alloc(alloc) + { + invalidate_leaf_cache(); + } + virtual ~BPlusTreeBase(); + + BPlusTreeBase& operator=(const BPlusTreeBase& rhs); + BPlusTreeBase& operator=(BPlusTreeBase&& rhs) noexcept; + + Allocator& get_alloc() const + { + return m_alloc; + } + + bool is_attached() const + { + return bool(m_root); + } + + size_t size() const + { + return m_size; + } + + bool is_empty() const + { + return m_size == 0; + } + + ref_type get_ref() const + { + REALM_ASSERT(is_attached()); + return m_root->get_ref(); + } + + void init_from_ref(ref_type ref) + { + auto new_root = create_root_from_ref(ref); + new_root->bp_set_parent(m_parent, m_ndx_in_parent); + + m_root = std::move(new_root); + + invalidate_leaf_cache(); + m_size = m_root->get_tree_size(); + } + + bool init_from_parent() + { + ref_type ref = m_parent->get_child_ref(m_ndx_in_parent); + if (!ref) { + return false; + } + auto new_root = create_root_from_ref(ref); + new_root->bp_set_parent(m_parent, m_ndx_in_parent); + m_root = std::move(new_root); + invalidate_leaf_cache(); + m_size = m_root->get_tree_size(); + return true; + } + + void set_parent(ArrayParent* parent, size_t ndx_in_parent) + { + m_parent = parent; + m_ndx_in_parent = ndx_in_parent; + if (is_attached()) + m_root->bp_set_parent(parent, ndx_in_parent); + } + + void create(); + void destroy(); + void verify() const + { + m_root->verify(); + } + +protected: + template + struct LeafTypeTrait { + using type = typename ColumnTypeTraits::cluster_leaf_type; + }; + + friend class BPlusTreeInner; + friend class BPlusTreeLeaf; + + std::unique_ptr m_root; + Allocator& m_alloc; + ArrayParent* m_parent = nullptr; + size_t m_ndx_in_parent = 0; + size_t m_size = 0; + size_t m_cached_leaf_begin; + size_t m_cached_leaf_end; + + void set_leaf_bounds(size_t b, size_t e) + { + m_cached_leaf_begin = b; + m_cached_leaf_end = e; + } + + void invalidate_leaf_cache() + { + m_cached_leaf_begin = size_t(-1); + m_cached_leaf_end = size_t(-1); + } + + void adjust_leaf_bounds(int incr) + { + m_cached_leaf_end += incr; + } + + void bptree_insert(size_t n, BPlusTreeNode::InsertFunc func); + void bptree_erase(size_t n, BPlusTreeNode::EraseFunc func); + + // Create an un-attached leaf node + virtual std::unique_ptr create_leaf_node() = 0; + // Create a leaf node and initialize it with 'ref' + virtual std::unique_ptr init_leaf_node(ref_type ref) = 0; + + // Initialize the leaf cache with 'mem' + virtual BPlusTreeLeaf* cache_leaf(MemRef mem) = 0; + void replace_root(std::unique_ptr new_root); + std::unique_ptr create_root_from_ref(ref_type ref); +}; + +template <> +struct BPlusTreeBase::LeafTypeTrait { + using type = ArrayKeyNonNullable; +}; + +template +struct SwapBufferType { + T val; + SwapBufferType(T v) + : val(v) + { + } + T get() + { + return val; + } +}; + +template <> +struct SwapBufferType { + std::string val; + bool n; + SwapBufferType(StringData v) + : val(v.data(), v.size()) + , n(v.is_null()) + { + } + StringData get() + { + return n ? StringData() : StringData(val); + } +}; + +template <> +struct SwapBufferType { + std::string val; + bool n; + SwapBufferType(BinaryData v) + : val(v.data(), v.size()) + , n(v.is_null()) + { + } + BinaryData get() + { + return n ? BinaryData() : BinaryData(val); + } +}; + +/*****************************************************************************/ +/* BPlusTree */ +/* Actual implementation of the BPlusTree to hold elements of type T. */ +/*****************************************************************************/ +template +class BPlusTree : public BPlusTreeBase { +public: + using LeafArray = typename LeafTypeTrait::type; + + /** + * Actual class for the leaves. Maps the abstract interface defined + * in BPlusTreeNode onto the specific array class + **/ + class LeafNode : public BPlusTreeLeaf, public LeafArray { + public: + LeafNode(BPlusTreeBase* tree) + : BPlusTreeLeaf(tree) + , LeafArray(tree->get_alloc()) + { + } + + void init_from_ref(ref_type ref) noexcept override + { + LeafArray::init_from_ref(ref); + } + + ref_type get_ref() const override + { + return LeafArray::get_ref(); + } + + void bp_set_parent(realm::ArrayParent* p, size_t n) override + { + LeafArray::set_parent(p, n); + } + + void update_parent() override + { + LeafArray::update_parent(); + } + + size_t get_node_size() const override + { + return LeafArray::size(); + } + + size_t get_tree_size() const override + { + return LeafArray::size(); + } + + void move(BPlusTreeNode* new_node, size_t ndx, int64_t) override + { + LeafNode* dst(static_cast(new_node)); + LeafArray::move(*dst, ndx); + } + }; + + BPlusTree(Allocator& alloc) + : BPlusTreeBase(alloc) + , m_leaf_cache(this) + { + } + + BPlusTree(const BPlusTree& other) + : BPlusTree(other.get_alloc()) + { + *this = other; + } + + BPlusTree(BPlusTree&& other) noexcept + : BPlusTree(other.get_alloc()) + { + *this = std::move(other); + } + + /********************* Assignment ********************/ + + BPlusTree& operator=(const BPlusTree& rhs) + { + this->BPlusTreeBase::operator=(rhs); + return *this; + } + + BPlusTree& operator=(BPlusTree&& rhs) noexcept + { + this->BPlusTreeBase::operator=(std::move(rhs)); + return *this; + } + + /************ Tree manipulation functions ************/ + + static T default_value(bool nullable = false) + { + return LeafArray::default_value(nullable); + } + + void add(T value) + { + insert(npos, value); + } + + void insert(size_t n, T value) + { + auto func = [value](BPlusTreeNode* node, size_t ndx) { + LeafNode* leaf = static_cast(node); + leaf->LeafArray::insert(ndx, value); + return leaf->size(); + }; + + bptree_insert(n, func); + m_size++; + } + + T get(size_t n) const + { + if (m_cached_leaf_begin <= n && n < m_cached_leaf_end) { + return m_leaf_cache.get(n - m_cached_leaf_begin); + } + else { + T value; + + auto func = [&value](BPlusTreeNode* node, size_t ndx) { + LeafNode* leaf = static_cast(node); + value = leaf->get(ndx); + }; + + m_root->bptree_access(n, func); + + return value; + } + } + + std::vector get_all() const + { + std::vector all_values; + all_values.reserve(m_size); + + auto func = [&all_values](BPlusTreeNode* node, size_t) { + LeafNode* leaf = static_cast(node); + size_t sz = leaf->size(); + for (size_t i = 0; i < sz; i++) { + all_values.push_back(leaf->get(i)); + } + return false; + }; + + m_root->bptree_traverse(func); + + return all_values; + } + + void set(size_t n, T value) + { + auto func = [value](BPlusTreeNode* node, size_t ndx) { + LeafNode* leaf = static_cast(node); + leaf->set(ndx, value); + }; + + m_root->bptree_access(n, func); + } + + void swap(size_t ndx1, size_t ndx2) + { + // We need two buffers. It is illegal to call set() with get() as argument + // in case of StingData and BinaryData. Source data may move or get overwritten + SwapBufferType tmp1{get(ndx1)}; + SwapBufferType tmp2{get(ndx2)}; + set(ndx1, tmp2.get()); + set(ndx2, tmp1.get()); + } + + void erase(size_t n) + { + auto func = [](BPlusTreeNode* node, size_t ndx) { + LeafNode* leaf = static_cast(node); + leaf->LeafArray::erase(ndx); + return leaf->size(); + }; + + bptree_erase(n, func); + m_size--; + } + + void clear() + { + if (m_root->is_leaf()) { + LeafNode* leaf = static_cast(m_root.get()); + leaf->clear(); + } + else { + destroy(); + create(); + if (m_parent) { + m_parent->update_child_ref(m_ndx_in_parent, get_ref()); + } + } + m_size = 0; + } + + void traverse(BPlusTreeNode::TraverseFunc func) const + { + if (m_root) { + m_root->bptree_traverse(func); + } + } + + size_t find_first(T value) const noexcept + { + size_t result = realm::npos; + + auto func = [&result, value](BPlusTreeNode* node, size_t offset) { + LeafNode* leaf = static_cast(node); + size_t sz = leaf->size(); + auto i = leaf->find_first(value, 0, sz); + if (i < sz) { + result = i + offset; + return true; + } + return false; + }; + + m_root->bptree_traverse(func); + + return result; + } + + template + void find_all(T value, Func&& callback) const noexcept + { + auto func = [&callback, value](BPlusTreeNode* node, size_t offset) { + LeafNode* leaf = static_cast(node); + size_t i = -1, sz = leaf->size(); + while ((i = leaf->find_first(value, i + 1, sz)) < sz) { + callback(i + offset); + } + return false; + }; + + m_root->bptree_traverse(func); + } + + void dump_values(std::ostream& o, int level) const + { + std::string indent(" ", level * 2); + + auto func = [&o, indent](BPlusTreeNode* node, size_t) { + LeafNode* leaf = static_cast(node); + size_t sz = leaf->size(); + for (size_t i = 0; i < sz; i++) { + o << indent << leaf->get(i) << std::endl; + } + return false; + }; + + m_root->bptree_traverse(func); + } + +protected: + LeafNode m_leaf_cache; + + /******** Implementation of abstract interface *******/ + + std::unique_ptr create_leaf_node() override + { + std::unique_ptr leaf = std::make_unique(this); + static_cast(leaf.get())->create(); + return leaf; + } + std::unique_ptr init_leaf_node(ref_type ref) override + { + std::unique_ptr leaf = std::make_unique(this); + leaf->init_from_ref(ref); + return leaf; + } + BPlusTreeLeaf* cache_leaf(MemRef mem) override + { + m_leaf_cache.init_from_mem(mem); + return &m_leaf_cache; + } + + template + friend R bptree_sum(const BPlusTree& tree); +}; + +template +inline bool bptree_aggregate_not_null(T) +{ + return true; +} +template +inline R bptree_aggregate_value(T val) +{ + return val; +} +template +inline bool bptree_aggregate_not_null(util::Optional val) +{ + return !!val; +} +template <> +inline bool bptree_aggregate_not_null(Timestamp val) +{ + return !val.is_null(); +} +inline bool bptree_aggregate_not_null(StringData val) +{ + return !val.is_null(); +} +inline bool bptree_aggregate_not_null(BinaryData val) +{ + return !val.is_null(); +} +template <> +inline bool bptree_aggregate_not_null(float val) +{ + return !null::is_null_float(val); +} +template <> +inline bool bptree_aggregate_not_null(double val) +{ + return !null::is_null_float(val); +} +template +inline T bptree_aggregate_value(util::Optional val) +{ + return *val; +} + +template +typename ColumnTypeTraits::sum_type bptree_sum(const BPlusTree& tree, size_t* return_cnt = nullptr) +{ + using ResultType = typename AggregateResultType::result_type; + ResultType result{}; + size_t cnt = 0; + + auto func = [&result, &cnt](BPlusTreeNode* node, size_t) { + auto leaf = static_cast::LeafNode*>(node); + size_t sz = leaf->size(); + for (size_t i = 0; i < sz; i++) { + auto val = leaf->get(i); + if (bptree_aggregate_not_null(val)) { + result += bptree_aggregate_value(val); + cnt++; + } + } + return false; + }; + + tree.traverse(func); + + if (return_cnt) + *return_cnt = cnt; + + return result; +} + +template +typename ColumnTypeTraits::minmax_type bptree_maximum(const BPlusTree& tree, size_t* return_ndx = nullptr) +{ + using ResultType = typename AggregateResultType::result_type; + ResultType max = std::numeric_limits::lowest(); + + auto func = [&max, return_ndx](BPlusTreeNode* node, size_t offset) { + auto leaf = static_cast::LeafNode*>(node); + size_t sz = leaf->size(); + for (size_t i = 0; i < sz; i++) { + auto val_or_null = leaf->get(i); + if (bptree_aggregate_not_null(val_or_null)) { + auto val = bptree_aggregate_value(val_or_null); + if (val > max) { + max = val; + if (return_ndx) { + *return_ndx = i + offset; + } + } + } + } + return false; + }; + + tree.traverse(func); + + return max; +} + +template +typename ColumnTypeTraits::minmax_type bptree_minimum(const BPlusTree& tree, size_t* return_ndx = nullptr) +{ + using ResultType = typename AggregateResultType::result_type; + ResultType min = std::numeric_limits::max(); + + auto func = [&min, return_ndx](BPlusTreeNode* node, size_t offset) { + auto leaf = static_cast::LeafNode*>(node); + size_t sz = leaf->size(); + for (size_t i = 0; i < sz; i++) { + auto val_or_null = leaf->get(i); + if (bptree_aggregate_not_null(val_or_null)) { + auto val = bptree_aggregate_value(val_or_null); + if (val < min) { + min = val; + if (return_ndx) { + *return_ndx = i + offset; + } + } + } + } + return false; + }; + + tree.traverse(func); + + return min; +} + +template +double bptree_average(const BPlusTree& tree, size_t* return_cnt = nullptr) +{ + size_t cnt; + auto sum = bptree_sum(tree, &cnt); + double avg{}; + if (cnt != 0) + avg = double(sum) / cnt; + if (return_cnt) + *return_cnt = cnt; + return avg; +} +} + +#endif /* REALM_BPLUSTREE_HPP */ diff --git a/src/vendor-include/realm-ios/include/realm/chunked_binary.hpp b/src/vendor-include/realm-ios/include/realm/chunked_binary.hpp new file mode 100644 index 000000000..1e76fa85e --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/chunked_binary.hpp @@ -0,0 +1,125 @@ +/************************************************************************* + * + * 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_NOINST_CHUNKED_BINARY_HPP +#define REALM_NOINST_CHUNKED_BINARY_HPP + +#include +#include +#include + +#include +#include + + +namespace realm { + +/// ChunkedBinaryData manages a vector of BinaryData. It is used to facilitate +/// extracting large binaries from binary columns and tables. +class ChunkedBinaryData { +public: + + ChunkedBinaryData(); + ChunkedBinaryData(const BinaryData& bd); + ChunkedBinaryData(const BinaryIterator& bd); + ChunkedBinaryData(const BinaryColumn& col, size_t index); + + /// size() returns the number of bytes in the chunked binary. + /// FIXME: This operation is O(n). + size_t size() const noexcept; + + /// is_null returns true if the chunked binary has zero chunks or if + /// the first chunk points to the nullptr. + bool is_null() const; + + /// FIXME: O(n) + char operator[](size_t index) const; + + std::string hex_dump(const char* separator = " ", int min_digits = -1) const; + + void write_to(util::ResettableExpandableBufferOutputStream& out) const; + + /// copy_to() copies the chunked binary data to \a buffer of size + /// \a buffer_size starting at \a offset in the ChunkedBinary. + /// copy_to() copies until the end of \a buffer or the end of + /// the ChunkedBinary whichever comes first. + /// copy_to() returns the number of copied bytes. + size_t copy_to(char* buffer, size_t buffer_size, size_t offset) const; + + /// copy_to() allocates a buffer of size() in \a dest and + /// copies the chunked binary data to \a dest. + size_t copy_to(std::unique_ptr& dest) const; + + /// get_first_chunk() is used in situations + /// where it is known that there is exactly one + /// chunk. This is the case if the ChunkedBinary + /// has been constructed from BinaryData. + BinaryData get_first_chunk() const; + +private: + BinaryIterator m_begin; + friend class ChunkedBinaryInputStream; +}; + +// FIXME: When ChunkedBinaryData is moved into Core, this should be moved as well. +class ChunkedBinaryInputStream : public _impl::NoCopyInputStream { +public: + explicit ChunkedBinaryInputStream(const ChunkedBinaryData& chunks) + : m_it(chunks.m_begin) + { + } + + bool next_block(const char*& begin, const char*& end) override + { + BinaryData block = m_it.get_next(); + begin = block.data(); + end = begin + block.size(); + return begin != end; + } + +private: + BinaryIterator m_it; +}; + + +/// Implementation: + + +inline ChunkedBinaryData::ChunkedBinaryData() +{ +} + +inline ChunkedBinaryData::ChunkedBinaryData(const BinaryData& bd) : m_begin{bd} +{ +} + +inline ChunkedBinaryData::ChunkedBinaryData(const BinaryIterator& bd) : m_begin{bd} +{ +} + +inline ChunkedBinaryData::ChunkedBinaryData(const BinaryColumn& col, size_t index) + : m_begin{&col, index} +{ +} + + +} // namespace realm + +#endif // REALM_NOINST_CHUNKED_BINARY_HPP diff --git a/src/vendor-include/realm-ios/include/realm/cluster.hpp b/src/vendor-include/realm-ios/include/realm/cluster.hpp new file mode 100644 index 000000000..3502bcca4 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/cluster.hpp @@ -0,0 +1,292 @@ +/************************************************************************* + * + * 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_CLUSTER_HPP +#define REALM_CLUSTER_HPP + +#include +#include +#include +#include +#include +#include + +namespace realm { + +class Spec; +class Table; +class ConstObj; +class Cluster; +class ClusterNodeInner; +class ClusterTree; +class ColumnAttrMask; +class CascadeState; + +struct FieldValue { + FieldValue(ColKey k, Mixed val) + : col_key(k) + , value(val) + { + } + ColKey col_key; + Mixed value; +}; + +using FieldValues = std::vector; + +class ClusterNode : public Array { +public: + // This structure is used to bring information back to the upper nodes when + // inserting new objects or finding existing ones. + struct State { + int64_t split_key; // When a node is split, this variable holds the value of the + // first key in the new node. (Relative to the key offset) + MemRef mem; // MemRef to the Cluster holding the new/found object + size_t index; // The index within the Cluster at which the object is stored. + }; + + struct IteratorState { + IteratorState(Cluster& leaf) + : m_current_leaf(leaf) + { + } + IteratorState(const IteratorState&); + void clear(); + void init(const ConstObj&); + + Cluster& m_current_leaf; + int64_t m_key_offset = 0; + size_t m_current_index = 0; + }; + + ClusterNode(uint64_t offset, Allocator& allocator, const ClusterTree& tree_top) + : Array(allocator) + , m_tree_top(tree_top) + , m_keys(allocator) + , m_offset(offset) + { + m_keys.set_parent(this, 0); + } + virtual ~ClusterNode() + { + } + void init_from_parent() + { + ref_type ref = get_ref_from_parent(); + char* header = m_alloc.translate(ref); + init(MemRef(header, ref, m_alloc)); + } + int64_t get_key_value(size_t ndx) const + { + return m_keys.get(ndx); + } + + virtual bool update_from_parent(size_t old_baseline) noexcept = 0; + virtual bool is_leaf() const = 0; + virtual int get_sub_tree_depth() const = 0; + virtual size_t node_size() const = 0; + /// Number of elements in this subtree + virtual size_t get_tree_size() const = 0; + /// Last key in this subtree + virtual int64_t get_last_key_value() const = 0; + virtual void ensure_general_form() = 0; + + /// Initialize node from 'mem' + virtual void init(MemRef mem) = 0; + /// Descend the tree from the root and copy-on-write the leaf + /// This will update all parents accordingly + virtual MemRef ensure_writeable(ObjKey k) = 0; + + /// Init and potentially Insert a column + virtual void insert_column(ColKey col) = 0; + /// Clear and potentially Remove a column + virtual void remove_column(ColKey col) = 0; + /// Return number of columns created. To be used by upgrade logic + virtual size_t nb_columns() const + { + return realm::npos; + } + /// Create a new object identified by 'key' and update 'state' accordingly + /// Return reference to new node created (if any) + virtual ref_type insert(ObjKey k, const FieldValues& init_values, State& state) = 0; + /// Locate object identified by 'key' and update 'state' accordingly + void get(ObjKey key, State& state) const; + /// Locate object identified by 'key' and update 'state' accordingly + /// Returns `false` if the object doesn't not exist. + virtual bool try_get(ObjKey key, State& state) const = 0; + /// Locate object identified by 'ndx' and update 'state' accordingly + virtual ObjKey get(size_t ndx, State& state) const = 0; + /// Return the index at which key is stored + virtual size_t get_ndx(ObjKey key, size_t ndx) const = 0; + + /// Erase element identified by 'key' + virtual size_t erase(ObjKey key, CascadeState& state) = 0; + + /// Nullify links pointing to element identified by 'key' + virtual void nullify_incoming_links(ObjKey key, CascadeState& state) = 0; + + /// Move elements from position 'ndx' to 'new_node'. The new node is supposed + /// to be a sibling positioned right after this one. All key values must + /// be subtracted 'key_adj' + virtual void move(size_t ndx, ClusterNode* new_leaf, int64_t key_adj) = 0; + + virtual void dump_objects(int64_t key_offset, std::string lead) const = 0; + + ObjKey get_real_key(size_t ndx) const + { + return ObjKey(get_key_value(ndx) + m_offset); + } + const ClusterKeyArray* get_key_array() const + { + return &m_keys; + } + void set_offset(uint64_t offs) + { + m_offset = offs; + } + uint64_t get_offset() const + { + return m_offset; + } + +protected: + const ClusterTree& m_tree_top; + ClusterKeyArray m_keys; + uint64_t m_offset; +}; + +class Cluster : public ClusterNode { +public: + Cluster(uint64_t offset, Allocator& allocator, const ClusterTree& tree_top) + : ClusterNode(offset, allocator, tree_top) + { + } + ~Cluster() override; + + void create(size_t nb_leaf_columns); // Note: leaf columns - may include holes + void init(MemRef mem) override; + bool update_from_parent(size_t old_baseline) noexcept override; + bool is_writeable() const + { + return !Array::is_read_only(); + } + MemRef ensure_writeable(ObjKey k) override; + + bool is_leaf() const override + { + return true; + } + int get_sub_tree_depth() const override + { + return 0; + } + static size_t node_size_from_header(Allocator& alloc, const char* header); + size_t node_size() const override + { + if (!is_attached()) { + return 0; + } + return m_keys.is_attached() ? m_keys.size() : get_size_in_compact_form(); + } + size_t get_tree_size() const override + { + return node_size(); + } + int64_t get_last_key_value() const override + { + auto sz = node_size(); + return sz ? get_key_value(sz - 1) : -1; + } + size_t lower_bound_key(ObjKey key) const + { + if (m_keys.is_attached()) { + return m_keys.lower_bound(uint64_t(key.value)); + } + else { + size_t sz = size_t(Array::get(0)) >> 1; + if (key.value < 0) + return 0; + if (size_t(key.value) > sz) + return sz; + } + return size_t(key.value); + } + + void adjust_keys(int64_t offset) + { + ensure_general_form(); + m_keys.adjust(0, m_keys.size(), offset); + } + + const Table* get_owning_table() const; + ColKey get_col_key(size_t ndx_in_parent) const; + + void ensure_general_form() override; + void insert_column(ColKey col) override; // Does not move columns! + void remove_column(ColKey col) override; // Does not move columns - may leave a 'hole' + size_t nb_columns() const override + { + return size() - s_first_col_index; + } + ref_type insert(ObjKey k, const FieldValues& init_values, State& state) override; + bool try_get(ObjKey k, State& state) const override; + ObjKey get(size_t, State& state) const override; + size_t get_ndx(ObjKey key, size_t ndx) const override; + size_t erase(ObjKey k, CascadeState& state) override; + void nullify_incoming_links(ObjKey key, CascadeState& state) override; + void upgrade_string_to_enum(ColKey col, ArrayString& keys); + + void init_leaf(ColKey col, ArrayPayload* leaf) const; + void add_leaf(ColKey col, ref_type ref); + + void verify() const; + void dump_objects(int64_t key_offset, std::string lead) const override; + +private: + static constexpr size_t s_key_ref_or_size_index = 0; + static constexpr size_t s_first_col_index = 1; + + size_t get_size_in_compact_form() const + { + return size_t(Array::get(s_key_ref_or_size_index)) >> 1; // Size is stored as tagged value + } + friend class ClusterTree; + void insert_row(size_t ndx, ObjKey k, const FieldValues& init_values); + void move(size_t ndx, ClusterNode* new_node, int64_t key_adj) override; + template + void do_create(ColKey col); + template + void do_insert_column(ColKey col, bool nullable); + template + void do_insert_row(size_t ndx, ColKey col, Mixed init_val, bool nullable); + template + void do_move(size_t ndx, ColKey col, Cluster* to); + template + void do_erase(size_t ndx, ColKey col); + void remove_backlinks(ObjKey origin_key, ColKey col, const std::vector& keys, CascadeState& state) const; + void do_erase_key(size_t ndx, ColKey col, CascadeState& state); + void do_insert_key(size_t ndx, ColKey col, Mixed init_val, ObjKey origin_key); + template + void set_spec(T&, ColKey::Idx) const; + template + void verify(ref_type ref, size_t index, util::Optional& sz) const; +}; + +} + +#endif /* SRC_REALM_CLUSTER_HPP_ */ diff --git a/src/vendor-include/realm-ios/include/realm/cluster_tree.hpp b/src/vendor-include/realm-ios/include/realm/cluster_tree.hpp new file mode 100644 index 000000000..20e74e7d1 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/cluster_tree.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_CLUSTER_TREE_HPP +#define REALM_CLUSTER_TREE_HPP + +#include +#include + +namespace realm { + +class ClusterTree { +public: + class ConstIterator; + class Iterator; + using TraverseFunction = util::FunctionRef; + using UpdateFunction = util::FunctionRef; + + ClusterTree(Table* owner, Allocator& alloc); + static MemRef create_empty_cluster(Allocator& alloc); + + ClusterTree(ClusterTree&&) = default; + + // Disable copying, this is not allowed. + ClusterTree& operator=(const ClusterTree&) = delete; + ClusterTree(const ClusterTree&) = delete; + + bool is_attached() const + { + return m_root->is_attached(); + } + Allocator& get_alloc() const + { + return m_alloc; + } + const Table* get_owner() const + { + return m_owner; + } + TableRef get_table_ref() const; + const Spec& get_spec() const; + + void init_from_ref(ref_type ref); + void init_from_parent(); + bool update_from_parent(size_t old_baseline) noexcept; + + size_t size() const noexcept + { + return m_size; + } + void clear(CascadeState&); + void nullify_links(ObjKey, CascadeState&); + bool is_empty() const noexcept + { + return size() == 0; + } + int64_t get_last_key_value() const + { + return m_root->get_last_key_value(); + } + MemRef ensure_writeable(ObjKey k) + { + return m_root->ensure_writeable(k); + } + Array& get_fields_accessor(Array& fallback, MemRef mem) const + { + if (m_root->is_leaf()) { + return *m_root; + } + fallback.init_from_mem(mem); + return fallback; + } + + uint64_t bump_content_version() + { + m_alloc.bump_content_version(); + return m_alloc.get_content_version(); + } + void bump_storage_version() + { + m_alloc.bump_storage_version(); + } + uint64_t get_content_version() const + { + return m_alloc.get_content_version(); + } + uint64_t get_instance_version() const + { + return m_alloc.get_instance_version(); + } + uint64_t get_storage_version(uint64_t inst_ver) const + { + return m_alloc.get_storage_version(inst_ver); + } + void insert_column(ColKey col) + { + m_root->insert_column(col); + } + void remove_column(ColKey col) + { + m_root->remove_column(col); + } + + // Insert entry for object, but do not create and return the object accessor + void insert_fast(ObjKey k, const FieldValues& init_values, ClusterNode::State& state); + // Create and return object + Obj insert(ObjKey k, const FieldValues&); + // Delete object with given key + void erase(ObjKey k, CascadeState& state); + // Check if an object with given key exists + bool is_valid(ObjKey k) const; + // Lookup and return read-only object + ConstObj get(ObjKey k) const; + // Lookup and return object + Obj get(ObjKey k); + // Lookup ContsObj by index + ConstObj get(size_t ndx) const; + // Lookup Obj by index + Obj get(size_t ndx); + // Get logical index of object identified by k + size_t get_ndx(ObjKey k) const; + // Find the leaf containing the requested object + bool get_leaf(ObjKey key, ClusterNode::IteratorState& state) const noexcept; + // Visit all leaves and call the supplied function. Stop when function returns true. + // Not allowed to modify the tree + bool traverse(TraverseFunction func) const; + // Visit all leaves and call the supplied function. The function can modify the leaf. + void update(UpdateFunction func); + + void enumerate_string_column(ColKey col_key); + void dump_objects() + { + m_root->dump_objects(0, ""); + } + void verify() const; + +private: + friend class ConstObj; + friend class Obj; + friend class Cluster; + friend class ClusterNodeInner; + Table* m_owner; + Allocator& m_alloc; + std::unique_ptr m_root; + size_t m_size = 0; + + void replace_root(std::unique_ptr leaf); + + std::unique_ptr create_root_from_mem(Allocator& alloc, MemRef mem); + std::unique_ptr create_root_from_ref(Allocator& alloc, ref_type ref) + { + return create_root_from_mem(alloc, MemRef{alloc.translate(ref), ref, alloc}); + } + std::unique_ptr get_node(ref_type ref) const; + + size_t get_column_index(StringData col_name) const; + void remove_all_links(CascadeState&); +}; + +class ClusterTree::ConstIterator { +public: + typedef std::output_iterator_tag iterator_category; + typedef const Obj value_type; + typedef ptrdiff_t difference_type; + typedef const Obj* pointer; + typedef const Obj& reference; + + ConstIterator(const ClusterTree& t, size_t ndx); + ConstIterator(const ConstIterator& other); + + ConstIterator& operator=(const ConstIterator& other) + { + REALM_ASSERT(&m_tree == &other.m_tree); + m_position = other.m_position; + m_key = other.m_key; + m_leaf_invalid = true; + + return *this; + } + + // If the object pointed to by the iterator is deleted, you will get an exception if + // you try to dereference the iterator before advancing it. + + // Random access relative to iterator position. + reference operator[](size_t n); + reference operator*() const + { + return *operator->(); + } + pointer operator->() const; + + // Advance the iterator to the next object in the table. This also holds if the object + // pointed to is deleted. That is - you will get the same result of advancing no matter + // if the previous object is deleted or not. + ConstIterator& operator++(); + + ConstIterator& operator+=(ptrdiff_t adj); + + ConstIterator operator+(ptrdiff_t adj) + { + return ConstIterator(m_tree, get_position() + adj); + } + bool operator==(const ConstIterator& rhs) const + { + return m_key == rhs.m_key; + } + bool operator!=(const ConstIterator& rhs) const + { + return m_key != rhs.m_key; + } + +protected: + const ClusterTree& m_tree; + mutable uint64_t m_storage_version = uint64_t(-1); + mutable Cluster m_leaf; + mutable ClusterNode::IteratorState m_state; + mutable uint64_t m_instance_version = uint64_t(-1); + ObjKey m_key; + mutable bool m_leaf_invalid; + mutable size_t m_position; + mutable size_t m_leaf_start_pos = size_t(-1); + mutable Obj m_obj; + + ObjKey load_leaf(ObjKey key) const; + size_t get_position(); +}; + +class ClusterTree::Iterator : public ClusterTree::ConstIterator { +public: + typedef std::forward_iterator_tag iterator_category; + typedef Obj value_type; + typedef Obj* pointer; + typedef Obj& reference; + + Iterator(const ClusterTree& t, size_t ndx) + : ConstIterator(t, ndx) + { + } + + reference operator*() const + { + return *operator->(); + } + pointer operator->() const + { + return const_cast(ConstIterator::operator->()); + } + Iterator& operator++() + { + return static_cast(ConstIterator::operator++()); + } + Iterator& operator+=(ptrdiff_t adj) + { + return static_cast(ConstIterator::operator+=(adj)); + } + Iterator operator+(ptrdiff_t adj) + { + return Iterator(m_tree, get_position() + adj); + } +}; +} + +#endif /* REALM_CLUSTER_TREE_HPP */ diff --git a/src/vendor-include/realm-ios/include/realm/column_binary.hpp b/src/vendor-include/realm-ios/include/realm/column_binary.hpp new file mode 100644 index 000000000..254f721c1 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/column_binary.hpp @@ -0,0 +1,78 @@ +/************************************************************************* + * + * 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 +#include + +namespace realm { + +class BinaryColumn : public BPlusTree { +public: + using BPlusTree::BPlusTree; + BinaryData get_at(size_t ndx, size_t& pos) const noexcept; +}; + +class BinaryIterator { +public: + BinaryIterator() + { + } + // TODO: When WriteLogCollector is removed, there is no need for this + BinaryIterator(BinaryData binary) + : m_binary(binary) + { + } + + BinaryIterator(const 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; + const BinaryColumn* m_binary_col = nullptr; + size_t m_ndx = 0; + size_t m_pos = 0; + BinaryData m_binary; +}; + +} // namespace realm + +#endif // REALM_COLUMN_BINARY_HPP diff --git a/src/vendor-include/realm-ios/include/realm/column_fwd.hpp b/src/vendor-include/realm-ios/include/realm/column_fwd.hpp new file mode 100644 index 000000000..bc5f583a1 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/column_fwd.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_COLUMN_FWD_HPP +#define REALM_COLUMN_FWD_HPP + +#include + +namespace realm { + +class IntegerColumn; +class IntegerColumnIterator; + +// Templated classes +template +class BPlusTree; + +namespace util { +template +class Optional; +} + +// Shortcuts, aka typedefs. +using DoubleColumn = BPlusTree; +using FloatColumn = BPlusTree; + +} // namespace realm + +#endif // REALM_COLUMN_FWD_HPP diff --git a/src/vendor-include/realm-ios/include/realm/column_integer.hpp b/src/vendor-include/realm-ios/include/realm/column_integer.hpp new file mode 100644 index 000000000..794fbf48f --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/column_integer.hpp @@ -0,0 +1,188 @@ +/************************************************************************* + * + * Copyright 2018 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_INTEGER_HPP +#define REALM_COLUMN_INTEGER_HPP + +#include +#include +#include + +namespace realm { + +class IntegerColumn; + +class IntegerColumnIterator { +public: + typedef std::bidirectional_iterator_tag iterator_category; + typedef int64_t value_type; + typedef ptrdiff_t difference_type; + typedef const value_type* pointer; + typedef const value_type& reference; + + IntegerColumnIterator(const IntegerColumn* tree, size_t pos) + : m_tree(tree) + , m_pos(pos) + { + } + + size_t get_position() const + { + return m_pos; + } + + int64_t operator->() const; + int64_t operator*() const; + int64_t operator[](size_t ndx) const; + + IntegerColumnIterator& operator++() + { + m_pos++; + return *this; + } + + IntegerColumnIterator operator++(int) + { + IntegerColumnIterator tmp(*this); + operator++(); + return tmp; + } + + IntegerColumnIterator& operator--() + { + m_pos--; + return *this; + } + + IntegerColumnIterator& operator+=(ptrdiff_t adj) + { + m_pos += adj; + return *this; + } + + IntegerColumnIterator& operator-=(ptrdiff_t adj) + { + m_pos -= adj; + return *this; + } + + IntegerColumnIterator operator+(ptrdiff_t adj) + { + return {m_tree, m_pos + adj}; + } + + IntegerColumnIterator operator-(ptrdiff_t adj) + { + return {m_tree, m_pos - adj}; + } + + IntegerColumnIterator operator--(int) + { + IntegerColumnIterator tmp(*this); + operator--(); + return tmp; + } + + ptrdiff_t operator-(const IntegerColumnIterator& rhs) + { + return m_pos - rhs.m_pos; + } + + bool operator!=(const IntegerColumnIterator& rhs) const + { + return m_pos != rhs.m_pos; + } + + bool operator==(const IntegerColumnIterator& rhs) const + { + return m_pos == rhs.m_pos; + } + + bool operator>(const IntegerColumnIterator& rhs) const + { + return m_pos > rhs.m_pos; + } + + bool operator<(const IntegerColumnIterator& rhs) const + { + return m_pos < rhs.m_pos; + } + + bool operator>=(const IntegerColumnIterator& rhs) const + { + return m_pos >= rhs.m_pos; + } + + bool operator<=(const IntegerColumnIterator& rhs) const + { + return m_pos <= rhs.m_pos; + } + +private: + const IntegerColumn* m_tree; + size_t m_pos; +}; + +class IntegerColumn : public BPlusTree { +public: + using const_iterator = IntegerColumnIterator; + + IntegerColumn(Allocator& alloc, ref_type ref = 0) + : BPlusTree(alloc) + { + if (ref != 0) + init_from_ref(ref); + } + + int64_t back() + { + return get(size() - 1); + } + IntegerColumnIterator cbegin() const + { + return IntegerColumnIterator(this, 0); + } + IntegerColumnIterator cend() const + { + return IntegerColumnIterator(this, size()); + } +}; + +inline int64_t IntegerColumnIterator::operator->() const +{ + return m_tree->get(m_pos); +} + +inline int64_t IntegerColumnIterator::operator*() const +{ + return m_tree->get(m_pos); +} + +inline int64_t IntegerColumnIterator::operator[](size_t ndx) const +{ + return m_tree->get(m_pos + ndx); +} + +inline std::ostream& operator<<(std::ostream& out, const IntegerColumnIterator& it) +{ + out << "IntegerColumnIterator at index: " << it.get_position(); + return out; +} +} + +#endif /* REALM_COLUMN_INTEGER_HPP */ diff --git a/src/vendor-include/realm-ios/include/realm/column_type.hpp b/src/vendor-include/realm-ios/include/realm/column_type.hpp new file mode 100644 index 000000000..2696582cf --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/column_type.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_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_OldStringEnum = 3, // double refs + col_type_Binary = 4, + col_type_OldTable = 5, + col_type_OldMixed = 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, + + /// Each element is a list of values + col_attr_List = 32 +}; + +class ColumnAttrMask { +public: + ColumnAttrMask() + : m_value(0) + { + } + bool test(ColumnAttr prop) + { + return (m_value & prop) != 0; + } + void set(ColumnAttr prop) + { + m_value |= prop; + } + void reset(ColumnAttr prop) + { + m_value &= ~prop; + } + bool operator==(const ColumnAttrMask& other) const + { + return m_value == other.m_value; + } + +private: + friend class Spec; + friend struct ColKey; + friend class Table; + int m_value; + ColumnAttrMask(int64_t val) + : m_value(int(val)) + { + } +}; + + +} // namespace realm + +#endif // REALM_COLUMN_TYPE_HPP diff --git a/src/vendor-include/realm-ios/include/realm/column_type_traits.hpp b/src/vendor-include/realm-ios/include/realm/column_type_traits.hpp new file mode 100644 index 000000000..a31fd447f --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/column_type_traits.hpp @@ -0,0 +1,258 @@ +/************************************************************************* + * + * 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 +#include + +namespace realm { + +struct ObjKey; +class Timestamp; +class ArraySmallBlobs; +class ArrayString; +class ArrayStringShort; +class ArrayBinary; +class ArrayTimestamp; +class ArrayInteger; +class ArrayRef; +class ArrayIntNull; +class ArrayBool; +class ArrayBoolNull; +class ArrayKey; +class ArrayKeyNonNullable; +template +class BasicArray; +template +class BasicArrayNull; +struct Link; +template +class Lst; + +template +struct ColumnTypeTraits; + +template +struct AggregateResultType { + using result_type = T; +}; + +template +struct AggregateResultType, action> { + using result_type = T; +}; + +template <> +struct AggregateResultType { + using result_type = double; +}; + +template <> +struct ColumnTypeTraits { + using leaf_type = ArrayInteger; + using cluster_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 cluster_leaf_type = ArrayRef; + static const DataType id = type_Int; + static const ColumnType column_id = col_type_Int; +}; + +template <> +struct ColumnTypeTraits> { + using leaf_type = ArrayIntNull; + using cluster_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 { + using cluster_leaf_type = ArrayBool; + static const DataType id = type_Bool; + static const ColumnType column_id = col_type_Bool; +}; + +template <> +struct ColumnTypeTraits> { + using cluster_leaf_type = ArrayBoolNull; + static const DataType id = type_Bool; + static const ColumnType column_id = col_type_Bool; +}; + +template <> +struct ColumnTypeTraits { + using cluster_leaf_type = ArrayKey; + static const DataType id = type_Link; + static const ColumnType column_id = col_type_Link; +}; + +template <> +struct ColumnTypeTraits { + static const ColumnType column_id = col_type_Link; +}; + +template <> +struct ColumnTypeTraits { + using cluster_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 cluster_leaf_type = BasicArrayNull; + 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 cluster_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> { + using cluster_leaf_type = BasicArrayNull; + 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 { + using cluster_leaf_type = ArrayTimestamp; + using minmax_type = Timestamp; + static const DataType id = type_Timestamp; + static const ColumnType column_id = col_type_Timestamp; +}; + +template <> +struct ColumnTypeTraits { + using cluster_leaf_type = ArrayString; + static const DataType id = type_String; + static const ColumnType column_id = col_type_String; +}; + +template <> +struct ColumnTypeTraits { + using leaf_type = ArraySmallBlobs; + using cluster_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 ColumnTypeTraits> { + static const ColumnType column_id = ColumnTypeTraits::column_id; +}; + +template +struct GetLeafType; +template <> +struct GetLeafType { + using type = ArrayInteger; +}; +template <> +struct GetLeafType { + using type = ArrayIntNull; +}; +template +struct GetLeafType { + // FIXME: Null definition + using type = BasicArray; +}; +template +struct GetLeafType { + // FIXME: Null definition + using type = BasicArray; +}; +template +struct GetLeafType { + // FIXME: Null definition + using type = ArrayTimestamp; +}; + +template +inline bool value_is_null(const T& val) +{ + return val.is_null(); +} +template +inline bool value_is_null(const util::Optional& val) +{ + return !val; +} +template <> +inline bool value_is_null(const int64_t&) +{ + return false; +} +template <> +inline bool value_is_null(const bool&) +{ + return false; +} +template <> +inline bool value_is_null(const float& val) +{ + return null::is_null_float(val); +} +template <> +inline bool value_is_null(const double& val) +{ + return null::is_null_float(val); +} +template <> +inline bool value_is_null(const ObjKey& val) +{ + return !val; +} + +} // namespace realm + +#endif // REALM_COLUMN_TYPE_TRAITS_HPP diff --git a/src/vendor-include/realm-ios/include/realm/data_type.hpp b/src/vendor-include/realm-ios/include/realm/data_type.hpp new file mode 100644 index 000000000..2685835fb --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/data_type.hpp @@ -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. + * + **************************************************************************/ + +#ifndef REALM_DATA_TYPE_HPP +#define REALM_DATA_TYPE_HPP + +#include + +namespace realm { + +class StringData; +class BinaryData; +class Timestamp; + +typedef int64_t Int; +typedef bool Bool; +typedef float Float; +typedef double Double; +typedef realm::StringData String; +typedef realm::BinaryData Binary; +typedef realm::Timestamp Timestamp; + + +// 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_OldTable = 5, + type_OldMixed = 6, + type_Link = 12, + type_LinkList = 13 +}; + +/// See Descriptor::set_link_type(). +enum LinkType { + link_Strong, + link_Weak, +}; + +const char* get_data_type_name(DataType type) noexcept; + +} // namespace realm + +#endif // REALM_DATA_TYPE_HPP diff --git a/src/vendor-include/realm-ios/include/realm/db.hpp b/src/vendor-include/realm-ios/include/realm/db.hpp new file mode 100644 index 000000000..c0f19877f --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/db.hpp @@ -0,0 +1,1012 @@ +/************************************************************************* + * + * 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace realm { + +namespace _impl { +class WriteLogCollector; +} + +class Transaction; +using TransactionRef = std::shared_ptr; + +/// Thrown by DB::create() 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 DB::create() if the type of history +/// (Replication::HistoryType) in the opened Realm file is incompatible with the +/// mode in which the Realm file is opened. For example, if there is a mismatch +/// between the history type in the file, and the history type associated with +/// the replication plugin passed to DB::create(). +/// +/// This exception will also be thrown if the history schema version is lower +/// than required, and no migration is possible +/// (Replication::is_upgradable_history_schema()). +struct IncompatibleHistories : util::File::AccessError { + IncompatibleHistories(const std::string& msg, const std::string& path) + : util::File::AccessError("Incompatible histories. " + msg, path) + { + } +}; + +/// The FileFormatUpgradeRequired exception can be thrown by the DB +/// 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. +/// It will also be thrown if a realm which requires upgrade is opened in read-only +/// mode (Group::open). +struct FileFormatUpgradeRequired : util::File::AccessError { + FileFormatUpgradeRequired(const std::string& msg, const std::string& path) + : util::File::AccessError(msg, path) + { + } +}; + + +/// A DB facilitates transactions. +/// +/// Access to a database is done through transactions. Transactions +/// are created by a DB object. No matter how many transactions you +/// use, you only need a single DB object per file. Methods on the DB +/// object are thread-safe. +/// +/// Realm has 3 types of Transactions: +/// * A frozen transaction allows read only access +/// * A read transaction allows read only access but can be promoted +/// to a write transaction. +/// * A write transaction allows write access. A write transaction can +/// be demoted to a read transaction. +/// +/// Frozen transactions are thread safe. Read and write transactions are not. +/// +/// Two processes that want to share a database file must reside on +/// the same host. +/// + +class DB; +using DBRef = std::shared_ptr; + +class DB : public std::enable_shared_from_this { +public: + // Create a DB and associate it with a file. DB Objects can only be associated with one file, + // the association determined on creation of the DB Object. The association can be broken by + // calling DB::close(), but after that no new association can be established. To reopen the + // file (or another file), a new DB object is needed. + static DBRef create(const std::string& file, bool no_create = false, const DBOptions options = DBOptions()); + static DBRef create(Replication& repl, const DBOptions options = DBOptions()); + + ~DB() noexcept; + + // Disable copying to prevent accessor errors. If you really want another + // instance, open another DB object on the same file. But you don't. + DB(const DB&) = delete; + DB& operator=(const DB&) = delete; + /// Close an open database. Calling close() is thread-safe with respect to + /// other calls to close and with respect to deleting transactions. + /// Calling close() while a write transaction is open is an error and close() + /// will throw a LogicError::wrong_transact_state. + /// Calling close() while a read transaction is open is by default treated + /// in the same way, but close(true) will allow the error to be ignored and + /// release resources despite open read transactions. + /// As successfull call to close() leaves transactions (and any associated + /// accessors) in a defunct state and the actual close() operation is not + /// interlocked with access through those accessors, so any access through accessors + /// may constitute a race with a call to close(). + /// Instead of using DB::close() to release resources, we recommend using transactions + /// to control release as follows: + /// * explicitly close() transactions at earliest time possible and + /// * explicitly nullify any DBRefs you may have. + void close(bool allow_open_read_transactions = false); + + bool is_attached() const noexcept; + + Allocator& get_alloc() + { + return m_alloc; + } + + Replication* get_replication() const + { + return m_replication; + } + + void set_replication(Replication* repl) noexcept + { + m_replication = repl; + } + + +#ifdef REALM_DEBUG + /// Deprecated method, only called from a unit test + /// + /// 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. + /// + /// 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); +#endif + + /// Querying for changes: + /// + /// NOTE: + /// "changed" means that one or more commits has been made to the database + /// since the presented transaction was made. + /// + /// 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(TransactionRef); + + /// 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(TransactionRef); + + /// release any thread waiting in wait_for_change(). + 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; + + /// Returns the version of the latest snapshot. + version_type get_version_of_latest_snapshot(); + + /// Thrown by start_read() if the specified version does not correspond to a + /// bound (AKA tethered) snapshot. + struct BadVersion; + + + /// Transactions are obtained from one of the following 3 methods: + TransactionRef start_read(VersionID = VersionID()); + TransactionRef start_frozen(VersionID = VersionID()); + // If nonblocking is true and a write transaction is already active, + // an invalid TransactionRef is returned. + TransactionRef start_write(bool nonblocking = false); + + + // report statistics of last commit done on THIS DB. + // 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. The locked space is the amount of memory + // that is free in current version, but being used in still live versions. + // Notice that we will always have two live versions - the current and the + // previous. + void get_stats(size_t& free_space, size_t& used_space, util::Optional locked_space = util::none) const; + //@} + + enum TransactStage { + transact_Ready, + transact_Reading, + transact_Writing, + transact_Frozen, + }; + + /// 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(); + + /// Get the size of the currently allocated slab area + size_t get_allocated_size() const; + + /// 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 DBs 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. + /// Likewise, attempts to create new transactions 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 + /// + /// If the output_encryption_key is `none` then the file's existing key will + /// be used (if any). If the output_encryption_key is nullptr, the resulting + /// file will be unencrypted. Any other value will change the encryption of + /// the file to the new 64 byte key. + /// + /// 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 DB 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 + /// + /// WARNING: Compact() is not thread-safe with respect to a concurrent close() + bool compact(bool bump_version_number = false, util::Optional output_encryption_key = util::none); + +#ifdef REALM_DEBUG + void test_ringbuf(); +#endif + +/// Once created, accessors belong to a transaction and can only be used for +/// access as long as that transaction is still active. Copies of accessors +/// can be created in association with another transaction, the importing transaction, +/// using said transactions import_copy_of method. This process is called +/// accessor import. Prior to Core 6, the corresponding mechanism was known +/// as "handover". +/// +/// For TableViews, there are 3 forms of import determined by the PayloadPolicy. +/// +/// - with payload move: the payload imported 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 +/// PayloadPolicy::Move +/// +/// - with payload copy: a copy of the payload is imported, 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 PayloadPolicy::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 PayloadPolicy::Stay. +/// +/// For all other (non-TableView) accessors, importing is done with payload +/// copy, since the payload is trivial. +/// +/// Importing *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. +/// +/// Importing *without* payload or with payload copy is guaranteed *not* to +/// change the accessors on the exporting side. +/// +/// Importing is *not* thread safe and should be carried out +/// by the thread that "owns" the involved accessors. +/// +/// Importing is transitive: +/// If the object being imported depends on other views +/// (table- or link- ), those objects will be imported as well. The mode +/// (payload copy, payload move, without payload) is applied +/// recursively. Note: If you are importing 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. + std::shared_ptr get_metrics() + { + return m_metrics; + } + + // Try to grab a exclusive lock of the given realm path's lock file. If the lock + // can be acquired, the callback will be executed with the lock and then return true. + // Otherwise false will be returned directly. + // The lock taken precludes races with other threads or processes accessing the + // files through a SharedGroup. + // It is safe to delete/replace realm files inside the callback. + // WARNING: It is not safe to delete the lock file in the callback. + using CallbackWithLock = std::function; + static bool call_with_lock(const std::string& realm_path, CallbackWithLock callback); + + // Return a list of files/directories core may use of the given realm file path. + // The first element of the pair in the returned list is the path string, the + // second one is to indicate the path is a directory or not. + // The temporary files are not returned by this function. + // It is safe to delete those returned files/directories in the call_with_lock's callback. + static std::vector> get_core_files(const std::string& realm_path); + +protected: + explicit DB(const DBOptions& options); // Is this ever used? + +private: + std::recursive_mutex m_mutex; + int m_transaction_count = 0; + SlabAlloc m_alloc; + Replication* m_replication = nullptr; + 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 ReadLockGuard; + + // Member variables + size_t m_free_space = 0; + size_t m_locked_space = 0; + size_t m_used_space = 0; + uint_fast32_t m_local_max_entry = 0; // highest version observed by this DB + std::vector m_local_locks_held; // tracks all read locks held by this DB + util::File m_file; + util::File::Map m_file_map; // Never remapped, provides access to everything but the ringbuffer + util::File::Map m_reader_map; // provides access to ringbuffer, remapped as needed when it grows + bool m_wait_for_change_enabled = true; // Initially wait_for_change is enabled + bool m_write_transaction_open = false; + std::string m_lockfile_path; + std::string m_lockfile_prefix; + std::string m_db_path; + std::string m_coordination_dir; + const char* m_key; + int m_file_format_version = 0; + util::InterprocessMutex m_writemutex; +#ifdef REALM_ASYNC_DAEMON + util::InterprocessMutex m_balancemutex; +#endif + util::InterprocessMutex m_controlmutex; +#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; + std::function m_upgrade_callback; + + std::shared_ptr m_metrics; + /// Attach this DB instance to the specified database file. + /// + /// While at least one instance of DB 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 DB 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 DBOptions for details of each option. + /// Sensible defaults are provided if this parameter is left out. + /// + /// \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 if \a DBOptions::allow_upgrade + /// is `false` and an upgrade is required. + /// + /// \throw UnsupportedFileFormatVersion if the file format version or + /// history schema version is one which this version of Realm does not know + /// how to migrate from. + void open(const std::string& file, bool no_create = false, const DBOptions options = DBOptions()); + + /// Open this group in replication mode. The specified Replication instance + /// must remain in existence for as long as the DB. + void open(Replication&, const DBOptions options = DBOptions()); + + + void do_open(const std::string& file, bool no_create, bool is_backend, const DBOptions options); + + Replication* const* get_repl() const noexcept + { + return &m_replication; + } + + // 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; + + // Release all read locks held by this DB object. After release, further calls to + // release_read_lock for locks already released must be avoided. + void release_all_read_locks() noexcept; + + /// return true if write transaction can commence, false otherwise. + bool do_try_begin_write(); + void do_begin_write(); + version_type do_commit(Transaction&); + void do_end_write() 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, Transaction& transaction); + + 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); + + int get_file_format_version() const noexcept; + + /// finish up the process of starting a write transaction. Internal use only. + void finish_begin_write(); + + void reset_free_space_tracking() + { + m_alloc.reset_free_space_tracking(); + } + + void close_internal(std::unique_lock, bool allow_open_read_transactions); + friend class Transaction; +}; + +inline void DB::get_stats(size_t& free_space, size_t& used_space, util::Optional locked_space) const +{ + free_space = m_free_space; + used_space = m_used_space; + if (locked_space) { + *locked_space = m_locked_space; + } +} + + +class Transaction : public Group { +public: + Transaction(DBRef _db, SlabAlloc* alloc, DB::ReadLockInfo& rli, DB::TransactStage stage); + // convenience, so you don't need to carry a reference to the DB around + ~Transaction(); + + DB::version_type get_version() const noexcept + { + return m_read_lock.m_version; + } + DB::version_type get_version_of_latest_snapshot() + { + return db->get_version_of_latest_snapshot(); + } + void close(); + bool is_attached() + { + return m_transact_stage != DB::transact_Ready && db->is_attached(); + } + + /// Get the approximate size of the data that would be written to the file if + /// a commit were done at this point. The reported size will always be bigger + /// than what will eventually be needed as we reserve a bit more memory that + /// will be needed. + size_t get_commit_size() const; + + DB::version_type commit(); + void rollback(); + void end_read(); + + // Live transactions state changes, often taking an observer functor: + DB::version_type commit_and_continue_as_read(); + template + void rollback_and_continue_as_read(O* observer); + void rollback_and_continue_as_read() + { + _impl::NullInstructionObserver o; + rollback_and_continue_as_read(&o); + } + template + void advance_read(O* observer, VersionID target_version = VersionID()); + void advance_read(VersionID target_version = VersionID()) + { + _impl::NullInstructionObserver o; + advance_read(&o, target_version); + } + template + bool promote_to_write(O* observer, bool nonblocking = false); + bool promote_to_write(bool nonblocking = false) + { + _impl::NullInstructionObserver o; + return promote_to_write(&o, nonblocking); + } + TransactionRef freeze(); + // Frozen transactions are created by freeze() or DB::start_frozen() + bool is_frozen() const noexcept override { return m_transact_stage == DB::transact_Frozen; } + TransactionRef duplicate(); + + _impl::History* get_history() const; + + // direct handover of accessor instances + Obj import_copy_of(const ConstObj& original); // slicing is OK for Obj/ConstObj + TableRef import_copy_of(const ConstTableRef original); + LnkLst import_copy_of(const ConstLnkLst& original); + LstBasePtr import_copy_of(const LstBase& original); + LnkLstPtr import_copy_of(const LnkLstPtr& original); + LnkLstPtr import_copy_of(const ConstLnkLstPtr& original); + + // handover of the heavier Query and TableView + std::unique_ptr import_copy_of(Query&, PayloadPolicy); + std::unique_ptr import_copy_of(TableView&, PayloadPolicy); + std::unique_ptr import_copy_of(ConstTableView&, PayloadPolicy); + + /// Get the current transaction type + DB::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(); + + void upgrade_file_format(int target_file_format_version); + +private: + DBRef get_db() const + { + return db; + } + + Replication* const* get_repl() const final + { + return db->get_repl(); + } + + template + bool internal_advance_read(O* observer, VersionID target_version, _impl::History&, bool); + void set_transact_stage(DB::TransactStage stage) noexcept; + void do_end_read() noexcept; + void commit_and_continue_writing(); + void initialize_replication(); + + DBRef db; + mutable std::unique_ptr<_impl::History> m_history_read; + mutable _impl::History* m_history = nullptr; + + DB::ReadLockInfo m_read_lock; + DB::TransactStage m_transact_stage = DB::transact_Ready; + + friend class DB; + friend class DisableReplication; +}; + +class DisableReplication { +public: + DisableReplication(Transaction& t) + : m_tr(t) + , m_owner(t.get_db()) + , m_repl(m_owner->get_replication()) + , m_version(t.get_version()) + { + m_owner->set_replication(nullptr); + t.get_version(); + t.m_history = nullptr; + } + + ~DisableReplication() + { + m_owner->set_replication(m_repl); + if (m_version != m_tr.get_version()) + m_tr.initialize_replication(); + } + +private: + Transaction& m_tr; + DBRef m_owner; + Replication* m_repl; + DB::version_type m_version; +}; + + +/* + * classes providing backward Compatibility with the older + * ReadTransaction and WriteTransaction types. + */ + +class ReadTransaction { +public: + ReadTransaction(DBRef sg) + : trans(sg->start_read()) + { + } + + ~ReadTransaction() noexcept + { + } + + operator Transaction&() + { + return *trans; + } + + bool has_table(StringData name) const noexcept + { + return trans->has_table(name); + } + + ConstTableRef get_table(TableKey key) const + { + return trans->get_table(key); // Throws + } + + ConstTableRef get_table(StringData name) const + { + return trans->get_table(name); // Throws + } + + const Group& get_group() const noexcept + { + return *trans.get(); + } + + /// Get the version of the snapshot to which this read transaction is bound. + DB::version_type get_version() const noexcept + { + return trans->get_version(); + } + +private: + TransactionRef trans; +}; + + +class WriteTransaction { +public: + WriteTransaction(DBRef sg) + : trans(sg->start_write()) + { + } + + ~WriteTransaction() noexcept + { + } + + operator Transaction&() + { + return *trans; + } + + bool has_table(StringData name) const noexcept + { + return trans->has_table(name); + } + + TableRef get_table(TableKey key) const + { + return trans->get_table(key); // Throws + } + + TableRef get_table(StringData name) const + { + return trans->get_table(name); // Throws + } + + TableRef add_table(StringData name) const + { + return trans->add_table(name); // Throws + } + + TableRef get_or_add_table(StringData name, bool* was_added = nullptr) const + { + return trans->get_or_add_table(name, was_added); // Throws + } + + Group& get_group() const noexcept + { + return *trans.get(); + } + + /// Get the version of the snapshot on which this write transaction is + /// based. + DB::version_type get_version() const noexcept + { + return trans->get_version(); + } + + DB::version_type commit() + { + return trans->commit(); + } + + void rollback() noexcept + { + trans->rollback(); + } + +private: + TransactionRef trans; +}; + + +// Implementation: + +struct DB::BadVersion : std::exception { +}; + +inline bool DB::is_attached() const noexcept +{ + return m_file_map.is_attached(); +} + +inline DB::TransactStage Transaction::get_transact_stage() const noexcept +{ + return m_transact_stage; +} + +class DB::ReadLockGuard { +public: + ReadLockGuard(DB& shared_group, ReadLockInfo& read_lock) noexcept + : m_shared_group(shared_group) + , m_read_lock(&read_lock) + { + } + ~ReadLockGuard() noexcept + { + if (m_read_lock) + m_shared_group.release_read_lock(*m_read_lock); + } + void release() noexcept + { + m_read_lock = 0; + } + +private: + DB& m_shared_group; + ReadLockInfo* m_read_lock; +}; + +template +inline void Transaction::advance_read(O* observer, VersionID version_id) +{ + if (m_transact_stage != DB::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); + + auto hist = get_history(); // Throws + if (!hist) + throw LogicError(LogicError::no_history); + + internal_advance_read(observer, version_id, *hist, false); // Throws +} + +template +inline bool Transaction::promote_to_write(O* observer, bool nonblocking) +{ + if (m_transact_stage != DB::transact_Reading) + throw LogicError(LogicError::wrong_transact_state); + + if (nonblocking) { + bool succes = db->do_try_begin_write(); + if (!succes) { + return false; + } + } + else { + db->do_begin_write(); // Throws + } + try { + Replication* repl = db->get_replication(); + if (!repl) + throw LogicError(LogicError::no_history); + + VersionID version = VersionID(); // Latest + m_history = repl->_get_history_write(); + bool history_updated = internal_advance_read(observer, version, *m_history, true); // Throws + + REALM_ASSERT(repl); // Presence of `repl` follows from the presence of `hist` + DB::version_type current_version = m_read_lock.m_version; + repl->initiate_transact(*this, 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(). + if (!m_top.is_attached()) + create_empty_group(); // Throws + } + catch (...) { + db->do_end_write(); + m_history = nullptr; + throw; + } + + set_transact_stage(DB::transact_Writing); + return true; +} + +template +inline void Transaction::rollback_and_continue_as_read(O* observer) +{ + if (m_transact_stage != DB::transact_Writing) + throw LogicError(LogicError::wrong_transact_state); + + Replication* repl = db->get_replication(); + if (!repl) + throw LogicError(LogicError::no_history); + + BinaryData uncommitted_changes = repl->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 + } + + // Mark all managed space (beyond the attached file) as free. + db->reset_free_space_tracking(); // 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); + advance_transact(top_ref, file_size, reversed_in, false); // Throws + + db->do_end_write(); + + repl->abort_transact(); + + m_history = nullptr; + set_transact_stage(DB::transact_Reading); +} + +template +inline bool Transaction::internal_advance_read(O* observer, VersionID version_id, _impl::History& hist, bool writable) +{ + DB::ReadLockInfo new_read_lock; + db->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) { + db->release_read_lock(new_read_lock); + // _impl::History::update_early_from_top_ref() was not called + // update allocator wrappers merely to update write protection + update_allocator_wrappers(writable); + return false; + } + + DB::ReadLockGuard g(*db, new_read_lock); + { + DB::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_alloc; + alloc.update_reader_view(new_file_size); + update_allocator_wrappers(writable); + using gf = _impl::GroupFriend; + // remap(new_file_size); // Throws + ref_type hist_ref = gf::get_history_ref(alloc, new_top_ref); + + hist.update_from_ref_and_version(hist_ref, new_version); + } + + 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; + DB::version_type old_version = m_read_lock.m_version; + DB::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. + + { + DB::version_type old_version = m_read_lock.m_version; + DB::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); + advance_transact(new_top_ref, new_file_size, in, writable); // Throws + } + g.release(); + db->release_read_lock(m_read_lock); + m_read_lock = new_read_lock; + + return true; // _impl::History::update_early_from_top_ref() was called +} + +inline int DB::get_file_format_version() const noexcept +{ + return m_file_format_version; +} + +} // namespace realm + +#endif // REALM_GROUP_SHARED_HPP diff --git a/src/vendor-include/realm-ios/include/realm/db_options.hpp b/src/vendor-include/realm-ios/include/realm/db_options.hpp new file mode 100644 index 000000000..829bada95 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/db_options.hpp @@ -0,0 +1,123 @@ +/************************************************************************* + * + * 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 DBOptions { + + /// 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. + Unsafe // If you use this, you loose ACID property + }; + + explicit DBOptions(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, bool track_metrics = false, + size_t metrics_history_size = 10000) + : durability(level) + , encryption_key(key) + , allow_file_format_upgrade(allow_upgrade) + , upgrade_callback(file_upgrade_callback) + , temp_dir(temp_directory) + , enable_metrics(track_metrics) + , metrics_buffer_size(metrics_history_size) + + { + } + + explicit DBOptions(const char* key) + : durability(Durability::Full) + , encryption_key(key) + , allow_file_format_upgrade(true) + , upgrade_callback(std::function()) + , temp_dir(sys_tmp_dir) + , enable_metrics(false) + , metrics_buffer_size(10000) + { + } + + /// 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; + + /// Controls the feature of collecting various metrics to the SharedGroup. + /// A prerequisite is compiling with REALM_METRICS=ON. + bool enable_metrics; + + /// The maximum number of entries stored by the metrics (if enabled). If this number + /// is exceeded without being consumed, only the most recent entries will be stored. + size_t metrics_buffer_size; + + /// 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/src/vendor-include/realm-ios/include/realm/disable_sync_to_disk.hpp b/src/vendor-include/realm-ios/include/realm/disable_sync_to_disk.hpp new file mode 100644 index 000000000..f642d6f17 --- /dev/null +++ b/src/vendor-include/realm-ios/include/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/src/vendor-include/realm-ios/include/realm/exceptions.hpp b/src/vendor-include/realm-ios/include/realm/exceptions.hpp new file mode 100644 index 000000000..d08e289e0 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/exceptions.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_EXCEPTIONS_HPP +#define REALM_EXCEPTIONS_HPP + +#include + +#include +#include + +namespace realm { + +using util::ExceptionWithBacktrace; + +/// Thrown by various functions to indicate that a specified table does not +/// exist. +class NoSuchTable : public ExceptionWithBacktrace { +public: + const char* message() const noexcept override; +}; + + +/// Thrown by various functions to indicate that a specified table name is +/// already in use. +class TableNameInUse : public ExceptionWithBacktrace { +public: + const char* message() 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 ExceptionWithBacktrace { +public: + const char* message() 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 ExceptionWithBacktrace { +public: + const char* message() const noexcept override; +}; + + +/// The UnsupportedFileFormatVersion exception is thrown by DB::open() +/// constructor when opening a database that uses a deprecated file format +/// and/or a deprecated history schema which this version of Realm cannot +/// upgrade from. +class UnsupportedFileFormatVersion : public ExceptionWithBacktrace { +public: + UnsupportedFileFormatVersion(int source_version); + /// The unsupported version of the file. + int source_version = 0; + const char* message() 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 ExceptionWithBacktrace { +public: + const char* message() 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. +}; + +/// Thrown when creating references that are too large to be contained in our ref_type (size_t) +class MaximumFileSizeExceeded : public std::runtime_error { +public: + MaximumFileSizeExceeded(const std::string& msg); + /// runtime_error::what() returns the msg provided in the constructor. +}; + +/// Thrown when writing fails because the disk is full. +class OutOfDiskSpace : public std::runtime_error { +public: + OutOfDiskSpace(const std::string& msg); + /// runtime_error::what() returns the msg provided in the constructor. +}; + +/// Thrown when a key can not be used (either not found or already existing +/// when trying to create a new object) +class InvalidKey : public std::runtime_error { +public: + InvalidKey(const std::string& msg) + : std::runtime_error(msg) + { + } +}; + +// SerialisationError intentionally does not inherit ExceptionWithBacktrace +// because the query-based-sync permissions queries generated on the server +// use a LinksToNode which is not currently serialisable (this limitation can +// be lifted in core 6 given stable ids). Coupled with query metrics which +// serialize all queries, the capturing of the stack for these frequent +// permission queries shows up in performance profiles. +class SerialisationError : public std::runtime_error { +public: + SerialisationError(const std::string& msg); + /// runtime_error::what() returns the msg provided in the constructor. +}; + +// thrown when a user constructed link path is not a valid input +class InvalidPathError : public std::runtime_error { +public: + InvalidPathError(const std::string& msg); + /// runtime_error::what() returns the msg provided in the constructor. +}; + +class DuplicatePrimaryKeyValueException : public std::logic_error { +public: + DuplicatePrimaryKeyValueException(std::string object_type, std::string property); + + std::string const& object_type() const + { + return m_object_type; + } + std::string const& property() const + { + return m_property; + } + +private: + std::string m_object_type; + std::string m_property; +}; + + +/// 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 ExceptionWithBacktrace { +public: + enum ErrorKind { + string_too_big, + binary_too_big, + table_name_too_long, + column_name_too_long, + column_name_in_use, + invalid_column_name, + 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, + + /// You can not add index on a subtable of a subtable + subtable_of_subtable_index, + + /// You try to instantiate a list object not matching column type + list_type_mismatch + }; + + LogicError(ErrorKind message); + + const char* message() 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::message() const noexcept +{ + return "No such table exists"; +} + +inline const char* TableNameInUse::message() const noexcept +{ + return "The specified table name is already in use"; +} + +inline const char* CrossTableLinkTarget::message() const noexcept +{ + return "Table is target of cross-table link columns"; +} + +inline const char* DescriptorMismatch::message() const noexcept +{ + return "Table descriptor mismatch"; +} + +inline UnsupportedFileFormatVersion::UnsupportedFileFormatVersion(int version) +: source_version(version) +{ +} + +inline const char* UnsupportedFileFormatVersion::message() const noexcept +{ + return "Database has an unsupported version and cannot be upgraded"; +} + +inline const char* MultipleSyncAgents::message() 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 MaximumFileSizeExceeded::MaximumFileSizeExceeded(const std::string& msg) + : std::runtime_error(msg) +{ +} + +inline OutOfDiskSpace::OutOfDiskSpace(const std::string& msg) + : std::runtime_error(msg) +{ +} + +inline SerialisationError::SerialisationError(const std::string& msg) + : std::runtime_error(msg) +{ +} + +inline InvalidPathError::InvalidPathError(const std::string& msg) + : 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/src/vendor-include/realm-ios/include/realm/global_key.hpp b/src/vendor-include/realm-ios/include/realm/global_key.hpp new file mode 100644 index 000000000..50223083c --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/global_key.hpp @@ -0,0 +1,161 @@ +/************************************************************************* + * + * Copyright 2019 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_GLOBAL_KEY_HPP +#define REALM_GLOBAL_KEY_HPP + +#include +#include +#include +#include + +namespace realm { + +class StringData; +class Mixed; + +/// GlobalKeys are globally unique for a given class (table), and up to 128 bits +/// wide. They are represented as two 64-bit integers, each of which may +/// frequently be small, for best on-wire compressibility. +/// +/// We define a way to map from 128-bit on-write GlobalKeys to local 64-bit ObjKeys. +/// +/// The three object ID types are: +/// a. Global Keyss for objects in tables without primary keys. +/// b. Global Keys for objects in tables with integer primary keys. +/// c. Global Keys for objects in tables with other primary key types. +/// +/// For objects without primary keys (a), a "squeezed" tuple of the +/// client_file_ident and a peer-local sequence number is used as the local +/// ObjKey. The on-write Object ID is the "unsqueezed" format. +/// +/// For integer primary keys (b), the GlobalKey just the integer value as the low +/// part. +/// +/// For objects with other types of primary keys (c), the GlobalKey is a 128-bit +/// hash of the primary key value. However, the local object ID must be a 63-bit +/// integer, because that is the maximum size integer that can be used in an ObjKey. +/// The solution is to optimistically use the lower 62 bits of the on-wire GlobalKey. +/// If this results in a ObjKey which is already in use, a new local ObjKey is +/// generated with the 63th bit set and using a locally generated sequence number for +/// the lower bits. The mapping between GlobalKey and ObjKey is stored in the Table +/// structure. + +struct GlobalKey { + constexpr GlobalKey(uint64_t h, uint64_t l) + : m_lo(l) + , m_hi(h) + { + } + static GlobalKey from_string(StringData); + + constexpr GlobalKey(realm::util::None = realm::util::none) + : m_lo(-1) + , m_hi(-1) + { + } + + // Construct an ObjectId from either a string or an integer + GlobalKey(Mixed pk); + + // Construct an object id from the local squeezed ObjKey + GlobalKey(ObjKey squeezed, uint64_t sync_file_id) + { + uint64_t u = uint64_t(squeezed.value); + + m_lo = (u & 0xff) | ((u & 0xffffff0000) >> 8); + m_hi = ((u & 0xff00) >> 8) | ((u & 0xffffff0000000000) >> 32); + if (m_hi == 0) + m_hi = sync_file_id; + } + + constexpr GlobalKey(const GlobalKey&) noexcept = default; + GlobalKey& operator=(const GlobalKey&) noexcept = default; + + constexpr uint64_t lo() const + { + return m_lo; + } + constexpr uint64_t hi() const + { + return m_hi; + } + + std::string to_string() const; + + constexpr bool operator<(const GlobalKey& other) const + { + return (m_hi == other.m_hi) ? (m_lo < other.m_lo) : (m_hi < other.m_hi); + } + constexpr bool operator==(const GlobalKey& other) const + { + return m_hi == other.m_hi && m_lo == other.m_lo; + } + constexpr bool operator!=(const GlobalKey& other) const + { + return !(*this == other); + } + + explicit constexpr operator bool() const noexcept + { + return (*this != GlobalKey{}); + } + + // Generate a local ObjKey from the GlobalKey. If the object is created + // in this realm (sync_file_id == hi) then 0 is used for hi. In this + // way we achieves that objects created before first contact with the + // server does not need to change key. + ObjKey get_local_key(uint64_t sync_file_id) + { + REALM_ASSERT(m_hi <= 0x3fffffff); + REALM_ASSERT(lo() <= std::numeric_limits::max()); + + auto high = m_hi; + if (high == sync_file_id) + high = 0; + uint64_t a = m_lo & 0xff; + uint64_t b = (high & 0xff) << 8; + uint64_t c = (m_lo & 0xffffff00) << 8; + uint64_t d = (high & 0x3fffff00) << 32; + + return ObjKey(int64_t(a | b | c | d)); + } + +private: + uint64_t m_lo; + uint64_t m_hi; +}; + +std::ostream& operator<<(std::ostream&, const GlobalKey&); +std::istream& operator>>(std::istream&, GlobalKey&); + +} // namespace realm + +namespace std { + +template <> +struct hash { + size_t operator()(realm::GlobalKey oid) const + { + return std::hash{}(oid.lo()) ^ std::hash{}(oid.hi()); + } +}; + +} // namespace std + +#endif /* REALM_OBJECT_ID_HPP */ diff --git a/src/vendor-include/realm-ios/include/realm/group.hpp b/src/vendor-include/realm-ios/include/realm/group.hpp new file mode 100644 index 000000000..e2259121b --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/group.hpp @@ -0,0 +1,1395 @@ +/************************************************************************* + * + * 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 +#include + +namespace realm { + +class DB; +class TableKeys; + +namespace _impl { +class GroupFriend; +class TransactLogConvenientEncoder; +class TransactLogParser; +} + + +/// A group is a collection of named tables. +/// +class Group : public ArrayParent { +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 Realm file that contains a history (see Replication::HistoryType) may + /// be opened via Group::open(), as long as the application can ensure that + /// there is no concurrent access to the file (see below for more on + /// concurrency), but if the file is modified via Group::commit() the + /// history will be discarded. To retain the history, the application must + /// instead access the file in shared mode, i.e., via SharedGroup, and + /// supply the right kind of replication plugin (see + /// Replication::get_history_type()). + /// + /// 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; + /// A group is frozen only if it is actually a frozen transaction. + virtual bool is_frozen() const noexcept { return false; } + /// Returns true if, and only if the number of tables in this + /// group is zero. + bool is_empty() const noexcept; + + size_t size() const noexcept; + + int get_history_schema_version() noexcept; + + Replication* get_replication() const + { + return *get_repl(); + } + + /// The sync file id is set when a client synchronizes with the server for the + /// first time. It is used when generating GlobalKeys for tables without a primary + /// key, where it is used as the "hi" part. This ensures global uniqueness of + /// GlobalKeys. + uint64_t get_sync_file_id() const noexcept; + void set_sync_file_id(uint64_t id); + + /// Returns the keys for all tables in this group. + TableKeys get_table_keys() const; + + /// \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 key 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 with the specified key. + /// + /// The versions of get_table(), that accepts a \a name argument, return a + /// 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 key assigned to it is returned. + /// + /// 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. + /// + /// 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. + /// + /// 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 key Key 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; + TableKey find_table(StringData name) const noexcept; + StringData get_table_name(TableKey key) const; + + TableRef get_table(TableKey key); + ConstTableRef get_table(TableKey key) const; + + // Catch some implicit conversions + TableRef get_table(int) = delete; + ConstTableRef get_table(int) const = delete; + + TableRef get_table(StringData name); + ConstTableRef get_table(StringData name) const; + + TableRef add_table(StringData name); + TableRef add_table_with_primary_key(StringData name, DataType pk_type, StringData pk_name, bool nullable = false); + TableRef get_or_add_table(StringData name, bool* was_added = nullptr); + + void remove_table(TableKey key); + void remove_table(StringData name); + + void rename_table(TableKey key, StringData new_name, bool require_unique_name = true); + void rename_table(StringData name, StringData new_name, bool require_unique_name = true); + + //@} + + // 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. + /// + /// \param version If different from 0, the new file will be a full fledged + /// realm file with free list and history info. The version of the commit + /// will be set to the value given here. + /// + /// \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, uint64_t version = 0, + bool write_history = true) 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 { + /// Key identifying a group-level table. + TableKey table_key; + + /// Key identifying object to be removed. + ObjKey key; + + row() = default; + + row(TableKey tk, ObjKey k) + : table_key(tk) + , key(k) + { + } + bool operator==(const row& r) const noexcept + { + return table_key == r.table_key && key == r.key; + } + bool operator!=(const row& r) const noexcept + { + return !(*this == r); + } + /// Trivial lexicographic order + bool operator<(const row& r) const noexcept + { + return table_key < r.table_key || (table_key == r.table_key && key < r.key); + } + }; + + struct link { + link() = default; + link(TableKey tk, ColKey ck, ObjKey k, ObjKey otk) + : origin_table(tk) + , origin_col_key(ck) + , origin_key(k) + , old_target_key(otk) + { + } + TableKey origin_table; ///< A group-level table. + ColKey origin_col_key; ///< Link column being nullified. + ObjKey origin_key; ///< 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. + ObjKey old_target_key; + }; + + /// 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; + + /// 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); + } + + /// Control of what to include when computing memory usage + enum SizeAggregateControl { + size_of_state = 1, ///< size of tables, indexes, toplevel array + size_of_history = 2, ///< size of the in-file history compartment + size_of_freelists = 4, ///< size of the freelists + size_of_all = 7 + }; + /// Compute the sum of the sizes in number of bytes of all the array nodes + /// that currently make up this group. When this group represents a snapshot + /// in a Realm file (such as during a read transaction via a SharedGroup + /// instance), this function computes the footprint of that snapshot within + /// the Realm file. + /// + /// If this group accessor is the detached state, this function returns + /// zero. + size_t compute_aggregated_byte_size(SizeAggregateControl ctrl = SizeAggregateControl::size_of_all) const noexcept; + /// Return the size taken up by the current snapshot. This is in contrast to + /// the number returned by SharedGroup::get_stats() which will return the + /// size of the last snapshot done in that SharedGroup. If the snapshots are + /// identical, the numbers will of course be equal. + size_t get_used_space() const noexcept; + + void verify() const; + void validate_primary_columns(); +#ifdef REALM_DEBUG + void print() const; + void print_free() const; + MemStats get_stats(); + void enable_mem_diagnostics(bool enable = true) + { + m_alloc.enable_debug(enable); + } +#endif + +protected: + virtual Replication* const* get_repl() const + { + return &Table::g_dummy_replication; + } + +private: + static constexpr char g_class_name_prefix[] = "class_"; + static constexpr size_t g_class_name_prefix_len = 6; + + // nullptr, if we're sharing an allocator provided during initialization + std::unique_ptr m_local_alloc; + // in-use allocator. If local, then equal to m_local_alloc. + SlabAlloc& m_alloc; + + int m_file_format_version; + /// `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; + Array m_tables; + ArrayStringShort m_table_names; + uint64_t m_last_seen_mapping_version = 0; + + typedef std::vector table_accessors; + mutable table_accessors m_table_accessors; + mutable std::mutex m_accessor_mutex; + mutable int m_num_tables = 0; + bool m_attached = false; + bool m_is_writable = true; + const bool m_is_shared; + + std::function m_notify_handler; + std::function m_schema_change_handler; + std::shared_ptr m_metrics; + size_t m_total_rows; + + class TableRecycler : public std::vector { + public: + ~TableRecycler() + { + for (auto t : *this) { + delete t; + } + } + }; + + static constexpr size_t s_table_name_ndx = 0; + static constexpr size_t s_table_refs_ndx = 1; + static constexpr size_t s_file_size_ndx = 2; + static constexpr size_t s_free_pos_ndx = 3; + static constexpr size_t s_free_size_ndx = 4; + static constexpr size_t s_free_version_ndx = 5; + static constexpr size_t s_version_ndx = 6; + static constexpr size_t s_hist_type_ndx = 7; + static constexpr size_t s_hist_ref_ndx = 8; + static constexpr size_t s_hist_version_ndx = 9; + static constexpr size_t s_sync_file_id_ndx = 10; + + static constexpr size_t s_group_max_size = 11; + + // We use the classic approach to construct a FIFO from two LIFO's, + // insertion is done into recycler_1, removal is done from recycler_2, + // and when recycler_2 is empty, recycler_1 is reversed into recycler_2. + // this i O(1) for each entry. + static TableRecycler g_table_recycler_1; + static TableRecycler g_table_recycler_2; + // number of tables held back before being recycled. We hold back recycling + // the latest to increase the probability of detecting race conditions + // without crashing. + const static int g_table_recycling_delay = 100; + static std::mutex g_table_recycler_mutex; + + struct shared_tag { + }; + Group(shared_tag) noexcept; + + Group(SlabAlloc* alloc) noexcept; + void init_array_parents() noexcept; + + void open(ref_type top_ref, const std::string& file_path); + + // If the underlying memory mappings have been extended, this method is used + // to update all the tables' allocator wrappers. The allocator wrappers are + // configure to either allow or deny changes. + void update_allocator_wrappers(bool writable); + + /// 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. + void attach(ref_type top_ref, bool writable, 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 remove_table(size_t table_ndx, TableKey key); + + 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, bool writable); + + /// 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; + + class TableWriter; + class DefaultTableWriter; + + static void write(std::ostream&, int file_format_version, TableWriter&, bool no_top_array, + bool pad_for_encryption, uint_fast64_t version_number); + + Table* do_get_table(size_t ndx); + const Table* do_get_table(size_t ndx) const; + Table* do_get_table(StringData name); + const Table* do_get_table(StringData name) const; + Table* do_add_table(StringData name); + + Table* do_get_or_add_table(StringData name, bool* was_added); + + void create_and_insert_table(TableKey key, StringData name); + Table* create_table_accessor(size_t table_ndx); + void recycle_table_accessor(Table*); + + void detach_table_accessors() noexcept; // Idempotent + + void mark_all_table_accessors() noexcept; + + void write(util::File& file, const char* encryption_key, uint_fast64_t version_number, bool write_history) const; + void write(std::ostream&, bool pad, uint_fast64_t version_numer, bool write_history) const; + + std::shared_ptr get_metrics() const noexcept; + void set_metrics(std::shared_ptr other) noexcept; + void update_num_objects(); + class TransactAdvancer; + void advance_transact(ref_type new_top_ref, size_t new_file_size, _impl::NoCopyInputStream&, bool writable); + void refresh_dirty_accessors(); + void flush_accessors_for_commit(); + + /// \brief The version of the format of the node structure (in file or in + /// memory) in use by Realm objects associated with this group. + /// + /// Every group contains a file format version field, which is returned + /// by this function. 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 and the value is used to determine if a file format + /// upgrade is required. + /// + /// 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. (When group is created + /// with the unattached_tag). The version number will then be determined in the + /// subsequent call to Group::open. + /// + /// 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 can get out of date, but only for a short while. It is always + /// guaranteed 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 the file format version must be changed to a proper + /// before the opening process completes (Group::open() or SharedGroup::open()). + /// + /// 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. + /// + /// 8 Subtables can now have search index. + /// + /// 9 Replication instruction values shuffled, instr_MoveRow added. + /// + /// 10 Memory mapping changes which require special treatment of large files + /// of preceeding versions. + /// + /// IMPORTANT: When introducing a new file format version, be sure to review + /// the file validity checks in Group::open() and SharedGroup::do_open, 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; + 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; + + 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; + void set_history_schema_version(int version); + template + void set_history_parent(Accessor& history_root) noexcept; + void prepare_top_for_history(int history_type, int history_schema_version, uint64_t file_ident); + template + void prepare_history_parent(Accessor& history_root, int history_type, int history_schema_version, + uint64_t file_ident); + static void validate_top_array(const Array& arr, const SlabAlloc& alloc); + + size_t find_table_index(StringData name) const noexcept; + TableKey ndx2key(size_t ndx) const; + size_t key2ndx(TableKey key) const; + size_t key2ndx_checked(TableKey key) const; + void set_size() const noexcept; + void remove_pk_table(); + void check_table_name_uniqueness(StringData name) + { + if (m_table_names.find_first(name) != not_found) + throw TableNameInUse(); + } + + friend class Table; + friend class GroupWriter; + friend class DB; + friend class _impl::GroupFriend; + friend class _impl::TransactLogConvenientEncoder; + friend class _impl::TransactLogParser; + friend class TrivialReplication; + friend class metrics::QueryInfo; + friend class metrics::Metrics; + friend class Transaction; + friend class TableKeyIterator; + friend class CascadeState; +}; + +class TableKeyIterator { +public: + bool operator!=(const TableKeyIterator& other) + { + return m_pos != other.m_pos; + } + TableKeyIterator& operator++(); + TableKey operator*(); + +private: + friend class TableKeys; + const Group* m_group; + size_t m_pos; + size_t m_index_in_group = 0; + TableKey m_table_key; + + TableKeyIterator(const Group* g, size_t p) + : m_group(g) + , m_pos(p) + { + } + void load_key(); +}; + +class TableKeys { +public: + TableKeys(const Group* g) + : m_iter(g, 0) + { + } + size_t size() const + { + return m_iter.m_group->size(); + } + bool empty() const + { + return size() == 0; + } + TableKey operator[](size_t p) const; + TableKeyIterator begin() const + { + return TableKeyIterator(m_iter.m_group, 0); + } + TableKeyIterator end() const + { + return TableKeyIterator(m_iter.m_group, size()); + } + +private: + mutable TableKeyIterator m_iter; +}; + +// Implementation + +inline TableKeys Group::get_table_keys() const +{ + return TableKeys(this); +} + +inline bool Group::is_attached() const noexcept +{ + return m_attached; +} + +inline bool Group::is_empty() const noexcept +{ + if (!is_attached()) + return false; + return size() == 0; +} + +inline size_t Group::key2ndx(TableKey key) const +{ + size_t idx = key.value & 0xFFFF; + return idx; +} + +inline StringData Group::get_table_name(TableKey key) const +{ + size_t table_ndx = key2ndx_checked(key); + return m_table_names.get(table_ndx); +} + +inline bool Group::has_table(StringData name) const noexcept +{ + size_t ndx = find_table_index(name); + return ndx != not_found; +} + +inline size_t Group::find_table_index(StringData name) const noexcept +{ + if (m_table_names.is_attached()) + return m_table_names.find_first(name); + return not_found; +} + +inline TableKey Group::find_table(StringData name) const noexcept +{ + if (!is_attached()) + return TableKey(); + size_t ndx = find_table_index(name); + return (ndx != npos) ? ndx2key(ndx) : TableKey{}; +} + +inline TableRef Group::get_table(TableKey key) +{ + if (!is_attached()) + throw LogicError(LogicError::detached_accessor); + auto ndx = key2ndx_checked(key); + Table* table = do_get_table(ndx); // Throws + return TableRef(table, table ? table->m_alloc.get_instance_version() : 0); +} + +inline ConstTableRef Group::get_table(TableKey key) const +{ + if (!is_attached()) + throw LogicError(LogicError::detached_accessor); + auto ndx = key2ndx_checked(key); + const Table* table = do_get_table(ndx); // Throws + return ConstTableRef(table, table ? table->m_alloc.get_instance_version() : 0); +} + +inline TableRef Group::get_table(StringData name) +{ + if (!is_attached()) + throw LogicError(LogicError::detached_accessor); + Table* table = do_get_table(name); // Throws + return TableRef(table, table ? table->m_alloc.get_instance_version() : 0); +} + +inline ConstTableRef Group::get_table(StringData name) const +{ + if (!is_attached()) + throw LogicError(LogicError::detached_accessor); + const Table* table = do_get_table(name); // Throws + return ConstTableRef(table, table ? table->m_alloc.get_instance_version() : 0); +} + +inline TableRef Group::add_table(StringData name) +{ + if (!is_attached()) + throw LogicError(LogicError::detached_accessor); + check_table_name_uniqueness(name); + Table* table = do_add_table(name); // Throws + return TableRef(table, table ? table->m_alloc.get_instance_version() : 0); +} + +inline TableRef Group::get_or_add_table(StringData name, bool* was_added) +{ + if (!is_attached()) + throw LogicError(LogicError::detached_accessor); + Table* table = do_get_or_add_table(name, was_added); // Throws + return TableRef(table, table ? table->m_alloc.get_instance_version() : 0); +} + +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 << "{" << std::endl; + + auto keys = get_table_keys(); + for (size_t i = 0; i < keys.size(); ++i) { + auto key = keys[i]; + StringData name = get_table_name(key); + std::map& m = *renames; + if (m[name] != "") + name = m[name]; + + ConstTableRef table = get_table(key); + + if (i) + out << ","; + out << "\"" << name << "\""; + out << ":"; + table->to_json(out, link_depth, renames); + out << std::endl; + } + + out << "}" << std::endl; +} + +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 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 +{ + REALM_ASSERT_DEBUG(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() > s_version_ndx) { + version_2 = version_type(top.get_as_ref_or_tagged(s_version_ndx).get_as_int()); + } + if (top.size() > s_hist_version_ndx) { + history_type_2 = int(top.get_as_ref_or_tagged(s_hist_type_ndx).get_as_int()); + history_schema_version_2 = int(top.get_as_ref_or_tagged(s_hist_version_ndx).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() > s_hist_type_ndx); + if (has_history) { + // This function is only used is shared mode (from SharedGroup) + REALM_ASSERT(top.size() > s_hist_version_ndx); + return top.get_as_ref(s_hist_ref_ndx); + } + return 0; +} + +inline int Group::get_history_schema_version() noexcept +{ + bool has_history = (m_top.is_attached() && m_top.size() > s_hist_type_ndx); + if (has_history) { + // This function is only used is shared mode (from SharedGroup) + REALM_ASSERT(m_top.size() > s_hist_version_ndx); + return int(m_top.get_as_ref_or_tagged(s_hist_version_ndx).get_as_int()); + } + return 0; +} + +inline void Group::set_sync_file_id(uint64_t id) +{ + while (m_top.size() < s_sync_file_id_ndx + 1) + m_top.add(0); + m_top.set(s_sync_file_id_ndx, RefOrTagged::make_tagged(id)); +} + +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 +} + +template +inline void Group::set_history_parent(Accessor& history_root) noexcept +{ + history_root.set_parent(&m_top, 8); +} + +template +void Group::prepare_history_parent(Accessor& history_root, int history_type, int history_schema_version, + uint64_t file_ident) +{ + prepare_top_for_history(history_type, history_schema_version, file_ident); + set_history_parent(history_root); +} + +class Group::TableWriter { +public: + struct HistoryInfo { + ref_type ref = 0; + int type = 0; + int version = 0; + uint64_t sync_file_id = 0; + }; + + virtual ref_type write_names(_impl::OutputStream&) = 0; + virtual ref_type write_tables(_impl::OutputStream&) = 0; + virtual HistoryInfo write_history(_impl::OutputStream&) = 0; + virtual ~TableWriter() noexcept + { + } +}; + +inline const Table* Group::do_get_table(size_t ndx) const +{ + return const_cast(this)->do_get_table(ndx); // Throws +} + +inline const Table* Group::do_get_table(StringData name) const +{ + return const_cast(this)->do_get_table(name); // Throws +} + +inline void Group::reset_free_space_tracking() +{ + // if used whith a shared allocator, free space should never be reset through + // Group, but rather through the proper owner of the allocator, which is the DB object. + REALM_ASSERT(m_local_alloc); + m_alloc.reset_free_space_tracking(); // Throws +} + +inline std::shared_ptr Group::get_metrics() const noexcept +{ + return m_metrics; +} + +inline void Group::set_metrics(std::shared_ptr shared) noexcept +{ + m_metrics = shared; +} + +// 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(const Group& group) noexcept + { + return group.m_alloc; + } + + static ref_type get_top_ref(const Group& group) noexcept + { + return group.m_top.get_ref(); + } + + 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 ref_type get_history_ref(const Group& group) noexcept + { + return Group::get_history_ref(group.m_top); + } + + static int get_file_format_version(const Group& group) noexcept + { + return group.get_file_format_version(); + } + + static void get_version_and_history_info(const Allocator& alloc, ref_type top_ref, + _impl::History::version_type& version, + int& history_type, + int& history_schema_version) noexcept + { + Array top{const_cast(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 void set_history_schema_version(Group& group, int version) + { + group.set_history_schema_version(version); // Throws + } + + template + static void set_history_parent(Group& group, Accessor& history_root) noexcept + { + group.set_history_parent(history_root); + } + + template + static void prepare_history_parent(Group& group, Accessor& history_root, int history_type, + int history_schema_version, uint64_t file_ident = 0) + { + group.prepare_history_parent(history_root, history_type, history_schema_version, file_ident); // Throws + } + + // This is used by upgrade functions in Sync + static Table* get_table_by_ndx(Group& group, size_t ndx) + { + return group.do_get_table(ndx); + } + + 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); + } +}; + + +class CascadeState { +public: + enum class Mode { + /// If we remove the last link to an object, delete that object, even if + /// the link we removed was not a strong link + All, + /// If we remove the last link to an object, delete that object only if + /// the link we removed was a strong link + Strong, + /// Never delete objects due to removing links + None + }; + + struct Link { + TableKey origin_table; ///< A group-level table. + ColKey origin_col_key; ///< Link column being nullified. + ObjKey origin_key; ///< 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. + ObjKey old_target_key; + }; + + CascadeState(Mode mode = Mode::Strong, Group* g = nullptr) noexcept + : m_mode(mode) + , m_group(g) + { + } + + /// Indicate which links to take action on. Either all, strong or none. + Mode m_mode; + + std::vector> m_to_be_deleted; + std::vector m_to_be_nullified; + Group* m_group = nullptr; + + bool notification_handler() const noexcept + { + return m_group && m_group->has_cascade_notification_handler(); + } + + void send_notifications(Group::CascadeNotification& notifications) const + { + REALM_ASSERT_DEBUG(notification_handler()); + m_group->send_cascade_notification(notifications); + } + + bool enqueue_for_cascade(const Obj& target_obj, bool link_is_strong, bool last_removed) + { + // Check if the object should be cascade deleted + if (m_mode == Mode::None && last_removed) { + return false; + } + if (m_mode == Mode::All || link_is_strong) { + bool has_backlinks = target_obj.has_backlinks(m_mode == Mode::Strong); + if (!has_backlinks) { + // Object has no more backlinks - add to list for deletion + m_to_be_deleted.emplace_back(target_obj.get_table()->get_key(), target_obj.get_key()); + return true; + } + } + return false; + } + + void enqueue_for_nullification(Table& src_table, ColKey src_col_key, ObjKey origin_key, ObjKey target_key) + { + // Nullify immediately if we don't need to send cascade notifications + if (!notification_handler()) { + Obj obj = src_table.get_object(origin_key); + obj.nullify_link(src_col_key, target_key); + return; + } + + // Otherwise enqueue it + m_to_be_nullified.push_back({src_table.get_key(), src_col_key, origin_key, target_key}); + } + + void send_notifications() + { + if (!notification_handler()) { + return; + } + Group::CascadeNotification notification; + for (auto& o : m_to_be_deleted) + notification.rows.emplace_back(o.first, o.second); + for (auto& l : m_to_be_nullified) + notification.links.emplace_back(l.origin_table, l.origin_col_key, l.origin_key, l.old_target_key); + send_notifications(notification); + } +}; + +} // namespace realm + +#endif // REALM_GROUP_HPP diff --git a/src/vendor-include/realm-ios/include/realm/group_writer.hpp b/src/vendor-include/realm-ios/include/realm/group_writer.hpp new file mode 100644 index 000000000..d2da7d396 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/group_writer.hpp @@ -0,0 +1,202 @@ +/************************************************************************* + * + * 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 +#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). + using Durability = DBOptions::Durability; + GroupWriter(Group&, Durability dura = Durability::Full); + ~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; + + ref_type write_array(const char*, size_t, uint32_t) override; + +#ifdef REALM_DEBUG + void dump(); +#endif + + size_t get_free_space_size() const + { + return m_free_space_size; + } + + size_t get_locked_space_size() const + { + return m_locked_space_size; + } + +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 = 0; + uint64_t m_readlock_version; + size_t m_window_alignment; + size_t m_free_space_size = 0; + size_t m_locked_space_size = 0; + Durability m_durability; + + struct FreeSpaceEntry { + FreeSpaceEntry(size_t r, size_t s, uint64_t v) + : ref(r) + , size(s) + , released_at_version(v) + { + } + size_t ref; + size_t size; + uint64_t released_at_version; + }; + class FreeList : public std::vector { + public: + FreeList() = default; + // Merge adjacent chunks + void merge_adjacent_entries_in_freelist(); + // Copy free space entries to structure where entries are sorted by size + void move_free_in_file_to_size_map(std::multimap& size_map); + }; + // m_free_in_file; + std::vector m_not_free_in_file; + std::multimap m_size_map; + using FreeListElement = std::multimap::iterator; + + void read_in_freelist(); + size_t recreate_freelist(size_t reserve_pos); + // 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(); + + /// 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. + FreeListElement reserve_free_space(size_t size); + + FreeListElement search_free_space_in_free_list_element(FreeListElement element, 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. + FreeListElement search_free_space_in_part_of_freelist(size_t size); + + /// 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. + FreeListElement extend_free_space(size_t requested_size); + + void write_array_at(MapWindow* window, ref_type, const char* data, size_t size); + FreeListElement split_freelist_chunk(FreeListElement, size_t alloc_pos); +}; + + +// 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/src/vendor-include/realm-ios/include/realm/handover_defs.hpp b/src/vendor-include/realm-ios/include/realm/handover_defs.hpp new file mode 100644 index 000000000..ad8bfba31 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/handover_defs.hpp @@ -0,0 +1,34 @@ +/************************************************************************* + * + * 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 + +#include + +namespace realm { + +enum class PayloadPolicy { Copy, Stay, Move }; + + +} // end namespace Realm + +#endif diff --git a/src/vendor-include/realm-ios/include/realm/history.hpp b/src/vendor-include/realm-ios/include/realm/history.hpp new file mode 100644 index 000000000..9710d0b8d --- /dev/null +++ b/src/vendor-include/realm-ios/include/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/src/vendor-include/realm-ios/include/realm/impl/array_writer.hpp b/src/vendor-include/realm-ios/include/realm/impl/array_writer.hpp new file mode 100644 index 000000000..f039ad505 --- /dev/null +++ b/src/vendor-include/realm-ios/include/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/src/vendor-include/realm-ios/include/realm/impl/clamped_hex_dump.hpp b/src/vendor-include/realm-ios/include/realm/impl/clamped_hex_dump.hpp new file mode 100644 index 000000000..353e54662 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/impl/clamped_hex_dump.hpp @@ -0,0 +1,49 @@ +/************************************************************************* + * + * 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_CLAMPED_HEX_DUMP_HPP +#define REALM_IMPL_CLAMPED_HEX_DUMP_HPP + +#include +#include + +namespace realm { +namespace _impl { + +/// Limit the amount of dumped data to 1024 bytes. For use in connection with +/// logging. +inline std::string clamped_hex_dump(BinaryData blob, std::size_t max_size = 1024) +{ + bool was_clipped = false; + std::size_t size_2 = blob.size(); + if (size_2 > max_size) { + size_2 = max_size; + was_clipped = true; + } + std::string str = util::hex_dump(blob.data(), size_2); // Throws + if (was_clipped) + str += "..."; // Throws + return str; +} + +} // namespace _impl +} // namespace realm + +#endif // REALM_IMPL_CLAMPED_HEX_DUMP_HPP diff --git a/src/vendor-include/realm-ios/include/realm/impl/clock.hpp b/src/vendor-include/realm-ios/include/realm/impl/clock.hpp new file mode 100644 index 000000000..fdf8aa3b6 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/impl/clock.hpp @@ -0,0 +1,54 @@ +/************************************************************************* + * + * 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_CLOCK_HPP +#define REALM_IMPL_CLOCK_HPP + +#include +#include + +#include + +namespace realm { +namespace _impl { + +inline sync::milliseconds_type realtime_clock_now() noexcept +{ + using clock = std::chrono::system_clock; + auto time_since_epoch = clock::now().time_since_epoch(); + auto millis_since_epoch = + std::chrono::duration_cast(time_since_epoch).count(); + return sync::milliseconds_type(millis_since_epoch); +} + + +inline sync::milliseconds_type monotonic_clock_now() noexcept +{ + using clock = std::chrono::steady_clock; + auto time_since_epoch = clock::now().time_since_epoch(); + auto millis_since_epoch = + std::chrono::duration_cast(time_since_epoch).count(); + return sync::milliseconds_type(millis_since_epoch); +} + +} // namespace _impl +} // namespace realm + +#endif // REALM_IMPL_CLOCK_HPP diff --git a/src/vendor-include/realm-ios/include/realm/impl/cont_transact_hist.hpp b/src/vendor-include/realm-ios/include/realm/impl/cont_transact_hist.hpp new file mode 100644 index 000000000..7e6c1e07d --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/impl/cont_transact_hist.hpp @@ -0,0 +1,140 @@ +/************************************************************************* + * + * 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_CONT_TRANSACT_HIST_HPP +#define REALM_IMPL_CONT_TRANSACT_HIST_HPP + +#include +#include + +#include +#include + +namespace realm { + +class Group; +class BinaryIterator; + +namespace _impl { + +/// Read-only access to history of changesets as needed to enable continuous +/// transactions. +class History { +public: + using version_type = VersionID::version_type; + + virtual ~History() noexcept {} + + /// May be called during any transaction + /// + /// It is a precondition for calls to this function that the reader view is + /// updated - that is, the mapping is updated to provide full visibility to + /// the file. + /// + virtual void update_from_ref_and_version(ref_type, version_type) = 0; + virtual void update_from_parent(version_type version) = 0; + + /// Get all changesets between the specified versions. References to those + /// changesets will be made available in successive entries of `buffer`. The + /// number of retrieved changesets is exactly `end_version - + /// begin_version`. If this number is greater than zero, the changeset made + /// available 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 + /// version 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 callee 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 successful 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 trimming 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 preceding 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; + + virtual void verify() const = 0; + + virtual void set_group(Group* group, bool updated = false) + { + m_group = group; + m_updated = updated; + } + + void ensure_updated(version_type version) const + { + if (!m_updated) { + const_cast(this)->update_from_parent(version); + m_updated = true; + } + } + +protected: + Group* m_group = nullptr; + +private: + mutable bool m_updated = false; +}; + +} // namespace _impl +} // namespace realm + +#endif // REALM_IMPL_CONTINUOUS_TRANSACTIONS_HISTORY_HPP diff --git a/src/vendor-include/realm-ios/include/realm/impl/destroy_guard.hpp b/src/vendor-include/realm-ios/include/realm/impl/destroy_guard.hpp new file mode 100644 index 000000000..f5b5e37f5 --- /dev/null +++ b/src/vendor-include/realm-ios/include/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/src/vendor-include/realm-ios/include/realm/impl/input_stream.hpp b/src/vendor-include/realm-ios/include/realm/impl/input_stream.hpp new file mode 100644 index 000000000..bc50140ac --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/impl/input_stream.hpp @@ -0,0 +1,256 @@ +/************************************************************************* + * + * 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 +#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/src/vendor-include/realm-ios/include/realm/impl/output_stream.hpp b/src/vendor-include/realm-ios/include/realm/impl/output_stream.hpp new file mode 100644 index 000000000..1d022cd53 --- /dev/null +++ b/src/vendor-include/realm-ios/include/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/src/vendor-include/realm-ios/include/realm/impl/simulated_failure.hpp b/src/vendor-include/realm-ios/include/realm/impl/simulated_failure.hpp new file mode 100644 index 000000000..4958151f6 --- /dev/null +++ b/src/vendor-include/realm-ios/include/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/src/vendor-include/realm-ios/include/realm/impl/transact_log.hpp b/src/vendor-include/realm-ios/include/realm/impl/transact_log.hpp new file mode 100644 index 000000000..86d4ec12d --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/impl/transact_log.hpp @@ -0,0 +1,1580 @@ +/************************************************************************* + * + * 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 + +namespace realm { + +struct GlobalKey; + +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_SelectTable = 10, + instr_CreateObject = 11, + instr_RemoveObject = 12, + instr_Set = 13, + instr_SetDefault = 14, + instr_ClearTable = 15, // Remove all rows in selected table + + instr_InsertColumn = 20, // Insert new column into to selected descriptor + instr_EraseColumn = 21, // Remove column from selected descriptor + instr_RenameColumn = 22, // Rename column in selected descriptor + instr_SetLinkType = 23, // Strong/weak + + instr_SelectList = 30, + instr_ListInsert = 31, // Insert list entry + instr_ListSet = 32, // Assign to list entry + instr_ListMove = 33, // Move an entry within a link list + instr_ListSwap = 34, // Swap two entries within a list + instr_ListErase = 35, // Remove an entry from a list + instr_ListClear = 36, // Remove all entries from a list +}; + +class TransactLogStream { +public: + virtual ~TransactLogStream() + { + } + + /// 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 size The required amount of contiguous free space. Must be + /// small (probably not greater than 1024) + /// \param out_free_begin must point to current write position which must be inside earlier + /// allocated area. Will be updated to point to new writing position. + /// \param out_free_end Will be updated to point to end of allocated area. + 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* get_data() const; + char* get_data(); + size_t get_size(); + +private: + 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(TableKey) + { + return true; + } + bool select_list(ColKey, ObjKey) + { + return true; + } + bool select_link_list(ColKey, ObjKey) + { + return true; + } + bool insert_group_level_table(TableKey) + { + return true; + } + bool erase_group_level_table(TableKey) + { + return true; + } + bool rename_group_level_table(TableKey) + { + return true; + } + + // Must have table selected: + bool create_object(ObjKey) + { + return true; + } + bool remove_object(ObjKey) + { + return true; + } + bool clear_table(size_t) + { + return true; + } + bool modify_object(ColKey, ObjKey) + { + return true; + } + bool list_set(size_t) + { + return true; + } + bool list_insert(size_t) + { + return true; + } + + // Must have descriptor selected: + bool insert_column(ColKey) + { + return true; + } + bool erase_column(ColKey) + { + return true; + } + bool rename_column(ColKey) + { + return true; + } + bool set_link_type(ColKey) + { + return true; + } + + // Must have linklist selected: + bool list_move(size_t, size_t) + { + return true; + } + bool list_swap(size_t, size_t) + { + return true; + } + bool list_erase(size_t) + { + return true; + } + bool 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(TableKey key); + bool insert_group_level_table(TableKey table_key); + bool erase_group_level_table(TableKey table_key); + bool rename_group_level_table(TableKey table_key); + + /// Must have table selected. + bool create_object(ObjKey); + bool remove_object(ObjKey); + bool modify_object(ColKey col_key, ObjKey key); + + bool clear_table(size_t old_table_size); + + // Must have descriptor selected: + bool insert_column(ColKey col_key); + bool erase_column(ColKey col_key); + bool rename_column(ColKey col_key); + bool set_link_type(ColKey col_key); + + // Must have linklist selected: + bool select_list(ColKey col_key, ObjKey key); + bool list_set(size_t list_ndx); + bool list_insert(size_t ndx); + bool list_move(size_t from_link_ndx, size_t to_link_ndx); + bool list_swap(size_t link1_ndx, size_t link2_ndx); + bool list_erase(size_t list_ndx); + bool 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: + // Make sure this is in agreement with the actual integer encoding + // scheme (see encode_int()). + static constexpr int max_enc_bytes_per_int = 10; +// 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 + + 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 + static char* encode_int(char*, T value); + friend class TransactLogParser; +}; + +class TransactLogConvenientEncoder { +public: + virtual ~TransactLogConvenientEncoder(); + virtual void add_class(StringData table_name); + virtual void add_class_with_primary_key(StringData table_name, DataType pk_type, StringData pk_field, + bool nullable); + virtual void insert_group_level_table(TableKey table_key, size_t num_tables, StringData name); + virtual void erase_group_level_table(TableKey table_key, size_t num_tables); + virtual void rename_group_level_table(TableKey table_key, StringData new_name); + virtual void insert_column(const Table*, ColKey col_key, DataType type, StringData name, LinkTargetInfo& link, + bool nullable = false, bool listtype = false, LinkType link_type = link_Weak); + virtual void erase_column(const Table*, ColKey col_key); + virtual void rename_column(const Table*, ColKey col_key, StringData name); + + virtual void set_int(const Table*, ColKey col_key, ObjKey key, int_fast64_t value, + Instruction variant = instr_Set); + virtual void add_int(const Table*, ColKey col_key, ObjKey key, int_fast64_t value); + virtual void set_bool(const Table*, ColKey col_key, ObjKey key, bool value, Instruction variant = instr_Set); + virtual void set_float(const Table*, ColKey col_key, ObjKey key, float value, Instruction variant = instr_Set); + virtual void set_double(const Table*, ColKey col_key, ObjKey key, double value, Instruction variant = instr_Set); + virtual void set_string(const Table*, ColKey col_key, ObjKey key, StringData value, + Instruction variant = instr_Set); + virtual void set_binary(const Table*, ColKey col_key, ObjKey key, BinaryData value, + Instruction variant = instr_Set); + virtual void set_timestamp(const Table*, ColKey col_key, ObjKey key, Timestamp value, + Instruction variant = instr_Set); + virtual void set_link(const Table*, ColKey col_key, ObjKey key, ObjKey value, Instruction variant = instr_Set); + virtual void set_null(const Table*, ColKey col_key, ObjKey key, Instruction variant = instr_Set); + virtual void insert_substring(const Table*, ColKey col_key, ObjKey key, size_t pos, StringData); + virtual void erase_substring(const Table*, ColKey col_key, ObjKey key, size_t pos, size_t size); + + virtual void list_set_int(const ConstLstBase& list, size_t list_ndx, int64_t value); + virtual void list_set_bool(const ConstLstBase& list, size_t list_ndx, bool value); + virtual void list_set_float(const ConstLstBase& list, size_t list_ndx, float value); + virtual void list_set_double(const ConstLstBase& list, size_t list_ndx, double value); + virtual void list_set_string(const Lst& list, size_t list_ndx, StringData value); + virtual void list_set_binary(const Lst& list, size_t list_ndx, BinaryData value); + virtual void list_set_timestamp(const Lst& list, size_t list_ndx, Timestamp value); + + virtual void list_insert_int(const ConstLstBase& list, size_t list_ndx, int64_t value); + virtual void list_insert_bool(const ConstLstBase& list, size_t list_ndx, bool value); + virtual void list_insert_float(const ConstLstBase& list, size_t list_ndx, float value); + virtual void list_insert_double(const ConstLstBase& list, size_t list_ndx, double value); + virtual void list_insert_string(const Lst& list, size_t list_ndx, StringData value); + virtual void list_insert_binary(const Lst& list, size_t list_ndx, BinaryData value); + virtual void list_insert_timestamp(const Lst& list, size_t list_ndx, Timestamp value); + + virtual void create_object(const Table*, GlobalKey); + virtual void create_object(const Table*, ObjKey); + virtual void create_object_with_primary_key(const Table*, GlobalKey, Mixed); + virtual void remove_object(const Table*, ObjKey); + /// \param prior_num_rows The number of rows in the table prior to the + /// modification. + virtual void set_link_type(const Table*, ColKey col_key, LinkType); + virtual void clear_table(const Table*, size_t prior_num_rows); + + virtual void list_set_null(const ConstLstBase&, size_t ndx); + virtual void list_insert_null(const ConstLstBase&, size_t ndx); + virtual void list_set_link(const Lst&, size_t link_ndx, ObjKey value); + virtual void list_insert_link(const Lst&, size_t link_ndx, ObjKey value); + virtual void list_move(const ConstLstBase&, size_t from_link_ndx, size_t to_link_ndx); + virtual void list_swap(const ConstLstBase&, size_t link_ndx_1, size_t link_ndx_2); + virtual void list_erase(const ConstLstBase&, size_t link_ndx); + virtual void list_clear(const ConstLstBase&); + + //@{ + + /// 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*, ColKey col_key, ObjKey key); + virtual void link_list_nullify(const Lst&, size_t link_ndx); + + //@} + +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: + struct LinkListId { + TableKey table_key; + ObjKey object_key; + ColKey col_id; + + LinkListId() = default; + LinkListId(const ConstLstBase& list) + : table_key(list.get_table()->get_key()) + , object_key(list.get_key()) + , col_id(list.get_col_key()) + { + } + LinkListId(TableKey t, ObjKey k, ColKey c) + : table_key(t) + , object_key(k) + , col_id(c) + { + } + bool operator!=(const LinkListId& other) + { + return object_key != other.object_key || table_key != other.table_key || col_id != other.col_id; + } + }; + TransactLogEncoder m_encoder; + mutable const Table* m_selected_table = nullptr; + mutable LinkListId m_selected_list; + + void unselect_all() noexcept; + void select_table(const Table*); // unselects link list + void select_list(const ConstLstBase&); + + void do_select_table(const Table*); + void do_select_list(const ConstLstBase&); + + void do_set(const Table*, ColKey col_key, ObjKey key, Instruction variant = instr_Set); + + friend class TransactReverser; +}; + + +class TransactLogParser { +public: + class BadTransactLog; // Exception + + TransactLogParser(); + ~TransactLogParser() noexcept; + + /// See `TransactLogEncoder` for a list of methods that the `InstructionHandler` must define. + 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; + + REALM_NORETURN void parser_error() const; + + template + void parse_one(InstructionHandler&); + bool has_next() noexcept; + + template + T read_int(); + + // 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 +}; + + +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 size, 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 used_size = *inout_new_begin - data; + m_buffer.reserve_extra(used_size, size); + data = m_buffer.data(); // May have changed + *inout_new_begin = data + used_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::get_data() const +{ + return m_buffer.data(); +} + +inline char* TransactLogBufferStream::get_data() +{ + return m_buffer.data(); +} + +inline size_t TransactLogBufferStream::get_size() +{ + return m_buffer.size(); +} + +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 +inline char* TransactLogEncoder::encode(char* ptr, T inst) +{ + return encode_int(ptr, inst); +} + +template <> +inline char* TransactLogEncoder::encode(char* ptr, TableKey key) +{ + return encode_int(ptr, key.value); +} + +template <> +inline char* TransactLogEncoder::encode(char* ptr, ColKey key) +{ + return encode_int(ptr, key.value); +} + +template <> +inline char* TransactLogEncoder::encode(char* ptr, ObjKey key) +{ + return encode_int(ptr, key.value); +} + +template <> +inline char* TransactLogEncoder::encode(char* ptr, Instruction inst) +{ + return encode_int(ptr, inst); +} + +template +size_t TransactLogEncoder::max_size(T) +{ + return max_enc_bytes_per_int; +} + +template <> +inline size_t TransactLogEncoder::max_size(Instruction) +{ + return 1; +} + +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...); +} + +inline void TransactLogConvenientEncoder::unselect_all() noexcept +{ + m_selected_table = nullptr; + m_selected_list = LinkListId(); +} + +inline void TransactLogConvenientEncoder::select_table(const Table* table) +{ + if (table != m_selected_table) + do_select_table(table); // Throws + m_selected_list = LinkListId(); +} + +inline void TransactLogConvenientEncoder::select_list(const ConstLstBase& list) +{ + if (LinkListId(list) != m_selected_list) { + do_select_list(list); // Throws + } +} + +inline bool TransactLogEncoder::insert_group_level_table(TableKey table_key) +{ + append_simple_instr(instr_InsertGroupLevelTable, table_key); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::insert_group_level_table(TableKey table_key, size_t, StringData) +{ + unselect_all(); + m_encoder.insert_group_level_table(table_key); // Throws +} + +inline bool TransactLogEncoder::erase_group_level_table(TableKey table_key) +{ + append_simple_instr(instr_EraseGroupLevelTable, table_key); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::erase_group_level_table(TableKey table_key, size_t) +{ + unselect_all(); + m_encoder.erase_group_level_table(table_key); // Throws +} + +inline bool TransactLogEncoder::rename_group_level_table(TableKey table_key) +{ + append_simple_instr(instr_RenameGroupLevelTable, table_key); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::rename_group_level_table(TableKey table_key, StringData) +{ + unselect_all(); + m_encoder.rename_group_level_table(table_key); // Throws +} + +inline bool TransactLogEncoder::insert_column(ColKey col_key) +{ + append_simple_instr(instr_InsertColumn, col_key); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::insert_column(const Table* t, ColKey col_key, DataType, StringData, + LinkTargetInfo&, bool, bool, LinkType) +{ + select_table(t); // Throws + m_encoder.insert_column(col_key); // Throws +} + +inline bool TransactLogEncoder::erase_column(ColKey col_key) +{ + append_simple_instr(instr_EraseColumn, col_key); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::erase_column(const Table* t, ColKey col_key) +{ + select_table(t); // Throws + m_encoder.erase_column(col_key); // Throws +} + +inline bool TransactLogEncoder::rename_column(ColKey col_key) +{ + append_simple_instr(instr_RenameColumn, col_key); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::rename_column(const Table* t, ColKey col_key, StringData) +{ + select_table(t); // Throws + m_encoder.rename_column(col_key); // Throws +} + + +inline bool TransactLogEncoder::modify_object(ColKey col_key, ObjKey key) +{ + append_simple_instr(instr_Set, col_key, key); // Throws + return true; +} + + +inline void TransactLogConvenientEncoder::do_set(const Table* t, ColKey col_key, ObjKey key, Instruction variant) +{ + if (variant != Instruction::instr_SetDefault) { + select_table(t); // Throws + m_encoder.modify_object(col_key, key); // Throws + } +} + + +inline void TransactLogConvenientEncoder::set_int(const Table* t, ColKey col_key, ObjKey key, int_fast64_t, + Instruction variant) +{ + do_set(t, col_key, key, variant); // Throws +} + + +inline void TransactLogConvenientEncoder::add_int(const Table* t, ColKey col_key, ObjKey key, int_fast64_t) +{ + do_set(t, col_key, key); // Throws +} + +inline void TransactLogConvenientEncoder::set_bool(const Table* t, ColKey col_key, ObjKey key, bool, + Instruction variant) +{ + do_set(t, col_key, key, variant); // Throws +} + +inline void TransactLogConvenientEncoder::set_float(const Table* t, ColKey col_key, ObjKey key, float, + Instruction variant) +{ + do_set(t, col_key, key, variant); // Throws +} + +inline void TransactLogConvenientEncoder::set_double(const Table* t, ColKey col_key, ObjKey key, double, + Instruction variant) +{ + do_set(t, col_key, key, variant); // Throws +} + +inline void TransactLogConvenientEncoder::set_string(const Table* t, ColKey col_key, ObjKey key, StringData, + Instruction variant) +{ + do_set(t, col_key, key, variant); // Throws +} + +inline void TransactLogConvenientEncoder::set_binary(const Table* t, ColKey col_key, ObjKey key, BinaryData, + Instruction variant) +{ + do_set(t, col_key, key, variant); // Throws +} + +inline void TransactLogConvenientEncoder::set_timestamp(const Table* t, ColKey col_key, ObjKey key, Timestamp, + Instruction variant) +{ + do_set(t, col_key, key, variant); // Throws +} + +inline void TransactLogConvenientEncoder::set_link(const Table* t, ColKey col_key, ObjKey key, ObjKey, + Instruction variant) +{ + do_set(t, col_key, key, variant); // Throws +} + +inline void TransactLogConvenientEncoder::set_null(const Table* t, ColKey col_key, ObjKey key, Instruction variant) +{ + do_set(t, col_key, key, variant); // Throws +} + +inline void TransactLogConvenientEncoder::nullify_link(const Table* t, ColKey col_key, ObjKey key) +{ + select_table(t); // Throws + m_encoder.modify_object(col_key, key); // Throws +} + +inline void TransactLogConvenientEncoder::insert_substring(const Table* t, ColKey col_key, ObjKey key, size_t, + StringData value) +{ + if (value.size() > 0) { + do_set(t, col_key, key); // Throws + } +} + +inline void TransactLogConvenientEncoder::erase_substring(const Table* t, ColKey col_key, ObjKey key, size_t, + size_t size) +{ + if (size > 0) { + do_set(t, col_key, key); // Throws + } +} + +inline bool TransactLogEncoder::list_set(size_t list_ndx) +{ + append_simple_instr(instr_ListSet, list_ndx); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::list_set_int(const ConstLstBase& list, size_t list_ndx, int64_t) +{ + select_list(list); // Throws + m_encoder.list_set(list_ndx); // Throws +} + +inline void TransactLogConvenientEncoder::list_set_bool(const ConstLstBase& list, size_t list_ndx, bool) +{ + select_list(list); // Throws + m_encoder.list_set(list_ndx); // Throws +} + +inline void TransactLogConvenientEncoder::list_set_float(const ConstLstBase& list, size_t list_ndx, float) +{ + select_list(list); // Throws + m_encoder.list_set(list_ndx); // Throws +} + +inline void TransactLogConvenientEncoder::list_set_double(const ConstLstBase& list, size_t list_ndx, double) +{ + select_list(list); // Throws + m_encoder.list_set(list_ndx); // Throws +} + +inline void TransactLogConvenientEncoder::list_set_string(const Lst& list, size_t list_ndx, StringData) +{ + select_list(list); // Throws + m_encoder.list_set(list_ndx); // Throws +} + +inline void TransactLogConvenientEncoder::list_set_binary(const Lst& list, size_t list_ndx, BinaryData) +{ + select_list(list); // Throws + m_encoder.list_set(list_ndx); // Throws +} + +inline void TransactLogConvenientEncoder::list_set_timestamp(const Lst& list, size_t list_ndx, Timestamp) +{ + select_list(list); // Throws + m_encoder.list_set(list_ndx); // Throws +} + +inline bool TransactLogEncoder::list_insert(size_t list_ndx) +{ + append_simple_instr(instr_ListInsert, list_ndx); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::list_insert_int(const ConstLstBase& list, size_t list_ndx, int64_t) +{ + select_list(list); // Throws + m_encoder.list_insert(list_ndx); // Throws +} + +inline void TransactLogConvenientEncoder::list_insert_bool(const ConstLstBase& list, size_t list_ndx, bool) +{ + select_list(list); // Throws + m_encoder.list_insert(list_ndx); // Throws +} + +inline void TransactLogConvenientEncoder::list_insert_float(const ConstLstBase& list, size_t list_ndx, float) +{ + select_list(list); // Throws + m_encoder.list_insert(list_ndx); // Throws +} + +inline void TransactLogConvenientEncoder::list_insert_double(const ConstLstBase& list, size_t list_ndx, double) +{ + select_list(list); // Throws + m_encoder.list_insert(list_ndx); // Throws +} + +inline void TransactLogConvenientEncoder::list_insert_string(const Lst& list, size_t list_ndx, StringData) +{ + select_list(list); // Throws + m_encoder.list_insert(list_ndx); // Throws +} + +inline void TransactLogConvenientEncoder::list_insert_binary(const Lst& list, size_t list_ndx, BinaryData) +{ + select_list(list); // Throws + m_encoder.list_insert(list_ndx); // Throws +} + +inline void TransactLogConvenientEncoder::list_insert_timestamp(const Lst& list, size_t list_ndx, + Timestamp) +{ + select_list(list); // Throws + m_encoder.list_insert(list_ndx); // Throws +} + +inline bool TransactLogEncoder::create_object(ObjKey key) +{ + append_simple_instr(instr_CreateObject, key); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::create_object(const Table* t, ObjKey key) +{ + select_table(t); // Throws + m_encoder.create_object(key); // Throws +} + +inline bool TransactLogEncoder::remove_object(ObjKey key) +{ + append_simple_instr(instr_RemoveObject, key); // Throws + return true; +} + + +inline void TransactLogConvenientEncoder::remove_object(const Table* t, ObjKey key) +{ + select_table(t); // Throws + m_encoder.remove_object(key); // Throws +} + +inline bool TransactLogEncoder::set_link_type(ColKey col_key) +{ + append_simple_instr(instr_SetLinkType, col_key); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::set_link_type(const Table* t, ColKey col_key, LinkType) +{ + select_table(t); // Throws + m_encoder.set_link_type(col_key); // Throws +} + + +inline bool TransactLogEncoder::clear_table(size_t old_size) +{ + append_simple_instr(instr_ClearTable, old_size); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::clear_table(const Table* t, size_t prior_num_rows) +{ + select_table(t); // Throws + m_encoder.clear_table(prior_num_rows); // Throws +} + +inline void TransactLogConvenientEncoder::list_set_null(const ConstLstBase& list, size_t list_ndx) +{ + select_list(list); // Throws + m_encoder.list_set(list_ndx); // Throws +} + +inline void TransactLogConvenientEncoder::list_insert_null(const ConstLstBase& list, size_t list_ndx) +{ + select_list(list); // Throws + m_encoder.list_insert(list_ndx); // Throws +} + +inline void TransactLogConvenientEncoder::list_set_link(const Lst& list, size_t link_ndx, ObjKey) +{ + select_list(list); // Throws + m_encoder.list_set(link_ndx); // Throws +} + +inline void TransactLogConvenientEncoder::list_insert_link(const Lst& list, size_t link_ndx, ObjKey) +{ + select_list(list); // Throws + m_encoder.list_insert(link_ndx); // Throws +} + +inline void TransactLogConvenientEncoder::link_list_nullify(const Lst& list, size_t link_ndx) +{ + select_list(list); // Throws + m_encoder.list_erase(link_ndx); // Throws +} + +inline bool TransactLogEncoder::list_move(size_t from_link_ndx, size_t to_link_ndx) +{ + REALM_ASSERT(from_link_ndx != to_link_ndx); + append_simple_instr(instr_ListMove, from_link_ndx, to_link_ndx); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::list_move(const ConstLstBase& list, size_t from_link_ndx, + size_t to_link_ndx) +{ + select_list(list); // Throws + m_encoder.list_move(from_link_ndx, to_link_ndx); // Throws +} + +inline bool TransactLogEncoder::list_swap(size_t link1_ndx, size_t link2_ndx) +{ + append_simple_instr(instr_ListSwap, link1_ndx, link2_ndx); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::list_swap(const ConstLstBase& list, size_t link1_ndx, size_t link2_ndx) +{ + select_list(list); // Throws + m_encoder.list_swap(link1_ndx, link2_ndx); // Throws +} + +inline bool TransactLogEncoder::list_erase(size_t list_ndx) +{ + append_simple_instr(instr_ListErase, list_ndx); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::list_erase(const ConstLstBase& list, size_t link_ndx) +{ + select_list(list); // Throws + m_encoder.list_erase(link_ndx); // Throws +} + +inline bool TransactLogEncoder::list_clear(size_t old_list_size) +{ + append_simple_instr(instr_ListClear, old_list_size); // Throws + return true; +} + +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 = 0; // silence a warning + 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_Set: { + ColKey col_key = ColKey(read_int()); // Throws + ObjKey key(read_int()); // Throws + if (!handler.modify_object(col_key, key)) // Throws + parser_error(); + return; + } + case instr_SetDefault: + // Should not appear in the transaction log + parser_error(); + case instr_ListSet: { + size_t list_ndx = read_int(); + if (!handler.list_set(list_ndx)) // Throws + parser_error(); + return; + } + case instr_CreateObject: { + ObjKey key(read_int()); // Throws + if (!handler.create_object(key)) // Throws + parser_error(); + return; + } + case instr_RemoveObject: { + ObjKey key(read_int()); // Throws + if (!handler.remove_object(key)) // Throws + parser_error(); + return; + } + case instr_SelectTable: { + int levels = read_int(); // Throws + REALM_ASSERT(levels == 0); + TableKey key = TableKey(read_int()); + if (!handler.select_table(key)) // Throws + parser_error(); + return; + } + case instr_ClearTable: { + size_t old_size = read_int(); // Throws + if (!handler.clear_table(old_size)) // Throws + parser_error(); + return; + } + case instr_ListInsert: { + size_t list_ndx = read_int(); + if (!handler.list_insert(list_ndx)) // Throws + parser_error(); + return; + } + case instr_ListMove: { + size_t from_link_ndx = read_int(); // Throws + size_t to_link_ndx = read_int(); // Throws + if (!handler.list_move(from_link_ndx, to_link_ndx)) // Throws + parser_error(); + return; + } + case instr_ListSwap: { + size_t link1_ndx = read_int(); // Throws + size_t link2_ndx = read_int(); // Throws + if (!handler.list_swap(link1_ndx, link2_ndx)) // Throws + parser_error(); + return; + } + case instr_ListErase: { + size_t link_ndx = read_int(); // Throws + if (!handler.list_erase(link_ndx)) // Throws + parser_error(); + return; + } + case instr_ListClear: { + size_t old_list_size = read_int(); // Throws + if (!handler.list_clear(old_list_size)) // Throws + parser_error(); + return; + } + case instr_SelectList: { + ColKey col_key = ColKey(read_int()); // Throws + ObjKey key = ObjKey(read_int()); // Throws + if (!handler.select_list(col_key, key)) // Throws + parser_error(); + return; + } + case instr_SetLinkType: { + ColKey col_key = ColKey(read_int()); // Throws + if (!handler.set_link_type(col_key)) // Throws + parser_error(); + return; + } + case instr_InsertColumn: { + ColKey col_key = ColKey(read_int()); // Throws + if (!handler.insert_column(col_key)) // Throws + parser_error(); + return; + } + case instr_EraseColumn: { + ColKey col_key = ColKey(read_int()); // Throws + if (!handler.erase_column(col_key)) // Throws + parser_error(); + return; + } + case instr_RenameColumn: { + ColKey col_key = ColKey(read_int()); // Throws + if (!handler.rename_column(col_key)) // Throws + parser_error(); + return; + } + case instr_InsertGroupLevelTable: { + TableKey table_key = TableKey(read_int()); // Throws + if (!handler.insert_group_level_table(table_key)) // Throws + parser_error(); + return; + } + case instr_EraseGroupLevelTable: { + TableKey table_key = TableKey(read_int()); // Throws + if (!handler.erase_group_level_table(table_key)) // Throws + parser_error(); + return; + } + case instr_RenameGroupLevelTable: { + TableKey table_key = TableKey(read_int()); // Throws + if (!handler.rename_group_level_table(table_key)) // 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 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; +} + + +class TransactReverser { +public: + bool select_table(TableKey key) + { + sync_table(); + m_encoder.select_table(key); + m_pending_ts_instr = get_inst(); + return true; + } + + bool insert_group_level_table(TableKey table_key) + { + sync_table(); + m_encoder.erase_group_level_table(table_key); + append_instruction(); + return true; + } + + bool erase_group_level_table(TableKey table_key) + { + sync_table(); + m_encoder.insert_group_level_table(table_key); + append_instruction(); + return true; + } + + bool rename_group_level_table(TableKey) + { + sync_table(); + return true; + } + + bool create_object(ObjKey key) + { + m_encoder.remove_object(key); // Throws + append_instruction(); + return true; + } + + bool remove_object(ObjKey key) + { + m_encoder.create_object(key); // Throws + append_instruction(); + return true; + } + + bool modify_object(ColKey col_key, ObjKey key) + { + m_encoder.modify_object(col_key, key); + append_instruction(); + return true; + } + + bool list_set(size_t ndx) + { + m_encoder.list_set(ndx); + append_instruction(); + return true; + } + + bool list_insert(size_t ndx) + { + m_encoder.list_erase(ndx); + append_instruction(); + return true; + } + + bool clear_table(size_t old_size) + { + while (old_size--) { + m_encoder.create_object(null_key); + append_instruction(); + } + return true; + } + + bool set_link_type(ColKey key) + { + m_encoder.set_link_type(key); + return true; + } + + bool insert_column(ColKey col_key) + { + m_encoder.erase_column(col_key); + append_instruction(); + return true; + } + + bool erase_column(ColKey col_key) + { + m_encoder.insert_column(col_key); + append_instruction(); + return true; + } + + bool rename_column(ColKey col_key) + { + m_encoder.rename_column(col_key); + return true; + } + + bool select_list(ColKey col_key, ObjKey key) + { + sync_list(); + m_encoder.select_list(col_key, key); + m_pending_ls_instr = get_inst(); + return true; + } + + bool list_move(size_t from_link_ndx, size_t to_link_ndx) + { + m_encoder.list_move(from_link_ndx, to_link_ndx); + append_instruction(); + return true; + } + + bool list_swap(size_t link1_ndx, size_t link2_ndx) + { + m_encoder.list_swap(link1_ndx, link2_ndx); + append_instruction(); + return true; + } + + bool list_erase(size_t list_ndx) + { + m_encoder.list_insert(list_ndx); + append_instruction(); + return true; + } + + bool 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.list_insert(i - 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_ls_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.get_data()); + return m_encoder.write_position() - m_buffer.get_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_list() + { + sync_select(m_pending_ls_instr); + } + + void sync_table() + { + sync_list(); + 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 into the buffer + reverser.sync_table(); + + m_buffer = reverser.m_buffer.get_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/src/vendor-include/realm-ios/include/realm/index_string.hpp b/src/vendor-include/realm-ios/include/realm/index_string.hpp new file mode 100644 index 000000000..a0394bc66 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/index_string.hpp @@ -0,0 +1,650 @@ +/************************************************************************* + * + * 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_Timestamp 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 ClusterColumn; + +template +class BPlusTree; + +/// Each StringIndex node contains an array of this type +class IndexArray : public Array { +public: + IndexArray(Allocator& allocator) + : Array(allocator) + { + } + + ObjKey index_string_find_first(StringData value, const ClusterColumn& column) const; + void index_string_find_all(IntegerColumn& result, StringData value, const ClusterColumn& column, + bool case_insensitive = false) const; + void index_string_find_all(std::vector& result, StringData value, const ClusterColumn& column, + bool case_insensitive = false) const; + FindRes index_string_find_all_no_copy(StringData value, const ClusterColumn& column, + InternalFindResult& result) const; + size_t index_string_count(StringData value, const ClusterColumn& column) const; + +private: + template + int64_t from_list(StringData value, InternalFindResult& result_ref, const IntegerColumn& key_values, + const ClusterColumn& column) const; + + void from_list_all(StringData value, std::vector& result, const IntegerColumn& rows, + const ClusterColumn& column) const; + + void from_list_all_ins(StringData value, std::vector& result, const IntegerColumn& rows, + const ClusterColumn& column) const; + + template + int64_t index_string(StringData value, InternalFindResult& result_ref, const ClusterColumn& column) const; + + void index_string_all(StringData value, std::vector& result, const ClusterColumn& column) const; + + void index_string_all_ins(StringData value, std::vector& result, const ClusterColumn& column) const; +}; + +// 12 is the biggest element size of any non-string/binary Realm type +constexpr size_t string_conversion_buffer_size = 12; +using StringConversionBuffer = std::array; + +// The purpose of this class is to get easy access to fields in a specific column in the +// cluster. When you have an object like this, you can get a string version of the relevant +// field based on the key for the object. +class ClusterColumn { +public: + ClusterColumn(const ClusterTree* cluster_tree, ColKey column_key) + : m_cluster_tree(cluster_tree) + , m_column_key(column_key) + { + } + size_t size() const + { + return m_cluster_tree->size(); + } + ClusterTree::ConstIterator begin() const + { + return ClusterTree::ConstIterator(*m_cluster_tree, 0); + } + + ClusterTree::ConstIterator end() const + { + return ClusterTree::ConstIterator(*m_cluster_tree, size()); + } + + + DataType get_data_type() const; + ColKey get_column_key() const + { + return m_column_key; + } + bool is_nullable() const; + StringData get_index_data(ObjKey key, StringConversionBuffer& buffer) const; + +private: + const ClusterTree* m_cluster_tree; + ColKey m_column_key; +}; + +class StringIndex { +public: + StringIndex(const ClusterColumn& target_column, Allocator&); + StringIndex(ref_type, ArrayParent*, size_t ndx_in_parent, const ClusterColumn& target_column, Allocator&); + ~StringIndex() noexcept + { + } + + ColKey get_column_key() const + { + return m_target_column.get_column_key(); + } + + template + static bool type_supported() + { + return realm::is_any::value; + } + static bool type_supported(realm::DataType type) + { + return (type == type_Int || type == type_String || type == type_Bool || type == type_Timestamp); + } + + static ref_type create_empty(Allocator& alloc); + + void set_target(const ClusterColumn& 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(const ClusterColumn& target_column); + ref_type get_ref() const noexcept; + + // StringIndex interface: + + bool is_empty() const; + + template + void insert(ObjKey key, T value); + template + void insert(ObjKey key, util::Optional value); + + template + void set(ObjKey key, T new_value); + template + void set(ObjKey key, util::Optional new_value); + + void erase(ObjKey key); + + template + ObjKey find_first(T value) const; + template + void find_all(std::vector& 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(BPlusTree& result) const; + bool has_duplicate_values() const noexcept; + + void verify() const; +#ifdef REALM_DEBUG + template + void verify_entries(const ClusterColumn& column) const; + void do_dump_node_structure(std::ostream&, int) const; + void to_dot() const; + void to_dot(std::ostream&, StringData title = StringData()) const; + void to_dot_2(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; + ClusterColumn m_target_column; + + struct inner_node_tag { + }; + StringIndex(inner_node_tag, Allocator&); + + static IndexArray* create_node(Allocator&, bool is_leaf); + + void insert_with_offset(ObjKey key, StringData value, size_t offset); + void insert_row_list(size_t ref, size_t offset, StringData value); + void insert_to_existing_list(ObjKey key, StringData value, IntegerColumn& list); + void insert_to_existing_list_at_lower(ObjKey key, StringData value, IntegerColumn& list, + const IntegerColumnIterator& lower); + key_type get_last_key() const; + + struct NodeChange { + size_t ref1; + size_t ref2; + enum ChangeType { change_None, change_InsertBefore, change_InsertAfter, change_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(change_None) + { + } + }; + + // B-Tree functions + void TreeInsert(ObjKey obj_key, key_type, size_t offset, StringData value); + NodeChange do_insert(ObjKey, key_type, size_t offset, StringData value); + /// Returns true if there is room or it can join existing entries + bool leaf_insert(ObjKey obj_key, 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(ObjKey key, StringData, size_t offset); + + StringData get(ObjKey key, 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); + 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(const ClusterTree* cluster_tree, ColKey column_key) + : m_column(cluster_tree, column_key) + { + } + SortedListComparator(const ClusterColumn& column) + : m_column(column) + { + } + + bool operator()(int64_t key_value, StringData needle); + bool operator()(StringData needle, int64_t key_value); + +private: + const ClusterColumn m_column; +}; + + +// Implementation: + +template +struct GetIndexData; + +template <> +struct GetIndexData { + static StringData get_index_data(const Timestamp& dt, StringConversionBuffer& buffer); +}; + +template <> +struct GetIndexData { + static StringData get_index_data(const int64_t& value, 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(const bool& value, StringConversionBuffer& buffer) + { + int64_t value2 = value ? 1 : 0; + const char* c = reinterpret_cast(&value2); + 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, StringConversionBuffer&) + { + return data; + } +}; + +template <> +struct GetIndexData { + static StringData get_index_data(null, StringConversionBuffer&) + { + return null{}; + } +}; + +template +struct GetIndexData> { + static StringData get_index_data(const util::Optional& value, StringConversionBuffer& buffer) + { + if (value) + return GetIndexData::get_index_data(*value, buffer); + return null{}; + } +}; + +template <> +struct GetIndexData { + static StringData get_index_data(float, StringConversionBuffer&) + { + REALM_ASSERT_RELEASE(false); // LCOV_EXCL_LINE; Index on float not supported + return {}; + } +}; + +template <> +struct GetIndexData { + static StringData get_index_data(double, StringConversionBuffer&) + { + REALM_ASSERT_RELEASE(false); // LCOV_EXCL_LINE; Index on float not supported + return {}; + } +}; + +template <> +struct GetIndexData { + static StringData get_index_data(BinaryData, StringConversionBuffer&) + { + REALM_ASSERT_RELEASE(false); // LCOV_EXCL_LINE; Index on float not supported + return {}; + } +}; + +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, StringConversionBuffer& buffer) +{ + return GetIndexData::type>::get_index_data(value, buffer); +} + + +inline StringIndex::StringIndex(const ClusterColumn& 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, + const ClusterColumn& 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(ClusterColumn(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(ObjKey key, T value) +{ + StringConversionBuffer buffer; + size_t offset = 0; // First key from beginning of string + insert_with_offset(key, to_str(value, buffer), offset); // Throws +} + +template +void StringIndex::insert(ObjKey key, util::Optional value) +{ + if (value) { + insert(key, *value); + } + else { + insert(key, null{}); + } +} + +template +void StringIndex::set(ObjKey key, T new_value) +{ + StringConversionBuffer buffer; + StringConversionBuffer buffer2; + StringData old_value = get(key, 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. + erase(key); // Throws + + size_t offset = 0; // First key from beginning of string + insert_with_offset(key, new_value2, offset); // Throws + } +} + +template +void StringIndex::set(ObjKey key, util::Optional new_value) +{ + if (new_value) { + set(key, *new_value); + } + else { + set(key, null{}); + } +} + +template +ObjKey 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(std::vector& 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(const ClusterColumn& target_column) +{ + m_array->init_from_parent(); + m_target_column = target_column; +} + +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/src/vendor-include/realm-ios/include/realm/keys.hpp b/src/vendor-include/realm-ios/include/realm/keys.hpp new file mode 100644 index 000000000..53ceba59a --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/keys.hpp @@ -0,0 +1,247 @@ +/************************************************************************* + * + * 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_KEYS_HPP +#define REALM_KEYS_HPP + +#include +#include +#include +#include + +namespace realm { + +struct TableKey { + static constexpr uint32_t null_value = uint32_t(-1) >> 1; // free top bit + constexpr TableKey() noexcept + : value(null_value) + { + } + explicit TableKey(uint32_t val) noexcept + : value(val) + { + } + TableKey& operator=(uint32_t val) noexcept + { + value = val; + return *this; + } + bool operator==(const TableKey& rhs) const noexcept + { + return value == rhs.value; + } + bool operator!=(const TableKey& rhs) const noexcept + { + return value != rhs.value; + } + bool operator<(const TableKey& rhs) const noexcept + { + return value < rhs.value; + } + explicit operator bool() const noexcept + { + return value != null_value; + } + uint32_t value; +}; + + +inline std::ostream& operator<<(std::ostream& os, TableKey tk) +{ + os << "TableKey(" << tk.value << ")"; + return os; +} + +namespace util { + +inline std::string to_string(TableKey tk) +{ + return to_string(tk.value); +} +} + +class TableVersions : public std::vector> { +public: + TableVersions() + { + } + TableVersions(TableKey key, uint64_t version) + { + emplace_back(key, version); + } + bool operator==(const TableVersions& other) const; +}; + +struct ColKey { + struct Idx { + unsigned val; + }; + + constexpr ColKey() noexcept + : value(uint64_t(-1) >> 1) // free top bit + { + } + constexpr explicit ColKey(int64_t val) noexcept + : value(val) + { + } + explicit ColKey(Idx index, ColumnType type, ColumnAttrMask attrs, unsigned tag) noexcept + : ColKey((index.val & 0xFFFFUL) | ((type & 0x3FUL) << 16) | ((attrs.m_value & 0xFFUL) << 22) | + ((tag & 0xFFFFFFFFUL) << 30)) + { + } + ColKey& operator=(int64_t val) noexcept + { + value = val; + return *this; + } + bool operator==(const ColKey& rhs) const noexcept + { + return value == rhs.value; + } + bool operator!=(const ColKey& rhs) const noexcept + { + return value != rhs.value; + } + bool operator<(const ColKey& rhs) const noexcept + { + return value < rhs.value; + } + bool operator>(const ColKey& rhs) const noexcept + { + return value > rhs.value; + } + explicit operator bool() const noexcept + { + return value != ColKey().value; + } + Idx get_index() const noexcept + { + return Idx{static_cast(value) & 0xFFFFU}; + } + ColumnType get_type() const noexcept + { + return ColumnType((static_cast(value) >> 16) & 0x3F); + } + ColumnAttrMask get_attrs() const noexcept + { + return ColumnAttrMask((static_cast(value) >> 22) & 0xFF); + } + unsigned get_tag() const noexcept + { + return (value >> 30) & 0xFFFFFFFFUL; + } + int64_t value; +}; + +inline std::ostream& operator<<(std::ostream& os, ColKey ck) +{ + os << "ColKey(" << ck.value << ")"; + return os; +} + +struct ObjKey { + constexpr ObjKey() noexcept + : value(-1) + { + } + explicit constexpr ObjKey(int64_t val) noexcept + : value(val) + { + } + ObjKey& operator=(int64_t val) noexcept + { + value = val; + return *this; + } + bool operator==(const ObjKey& rhs) const noexcept + { + return value == rhs.value; + } + bool operator!=(const ObjKey& rhs) const noexcept + { + return value != rhs.value; + } + bool operator<(const ObjKey& rhs) const noexcept + { + return value < rhs.value; + } + bool operator>(const ObjKey& rhs) const noexcept + { + return value > rhs.value; + } + explicit operator bool() const noexcept + { + return value != -1; + } + int64_t value; + +private: + // operator bool will enable casting to integer. Prevent this. + operator int64_t() const = delete; +}; + +class ObjKeys : public std::vector { +public: + ObjKeys(const std::vector& init) + { + reserve(init.size()); + for (auto i : init) { + emplace_back(i); + } + } + ObjKeys() + { + } +}; + + +inline std::ostream& operator<<(std::ostream& ostr, ObjKey key) +{ + ostr << "ObjKey(" << key.value << ")"; + return ostr; +} + +constexpr ObjKey null_key; + +namespace util { + +inline std::string to_string(ColKey ck) +{ + return to_string(ck.value); +} + +} // namespace util + +} // namespace realm + + +namespace std { + +template <> +struct hash { + size_t operator()(realm::ObjKey key) const + { + return std::hash{}(key.value); + } +}; + +} // namespace std + + +#endif diff --git a/src/vendor-include/realm-ios/include/realm/list.hpp b/src/vendor-include/realm-ios/include/realm/list.hpp new file mode 100644 index 000000000..63b4abffa --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/list.hpp @@ -0,0 +1,962 @@ +/************************************************************************* + * + * 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_LIST_HPP +#define REALM_LIST_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(disable : 4250) // Suppress 'inherits ... via dominance' on MSVC +#endif + +namespace realm { + +class TableView; +class SortDescriptor; +class Group; + +// To be used in query for size. Adds nullability to size so that +// it can be put in a NullableVector +struct SizeOfList { + static constexpr size_t null_value = size_t(-1); + + SizeOfList(size_t s = null_value) + : sz(s) + { + } + bool is_null() + { + return sz == null_value; + } + void set_null() + { + sz = null_value; + } + size_t size() const + { + return sz; + } + size_t sz = null_value; +}; + +inline std::ostream& operator<<(std::ostream& ostr, SizeOfList size_of_list) +{ + if (size_of_list.is_null()) { + ostr << "null"; + } + else { + ostr << size_of_list.sz; + } + return ostr; +} + +class ConstLstBase : public ArrayParent { +public: + ConstLstBase(ConstLstBase&&) = delete; + virtual ~ConstLstBase(); + /* + * Operations that makes sense without knowing the specific type + * can be made virtual. + */ + virtual size_t size() const = 0; + virtual bool is_null(size_t ndx) const = 0; + virtual Mixed get_any(size_t ndx) const = 0; + + virtual Mixed min(size_t* return_ndx = nullptr) const = 0; + virtual Mixed max(size_t* return_ndx = nullptr) const = 0; + virtual Mixed sum(size_t* return_cnt = nullptr) const = 0; + virtual Mixed avg(size_t* return_cnt = nullptr) const = 0; + + // Modifies a vector of indices so that they refer to values sorted according + // to the specified sort order + virtual void sort(std::vector& indices, bool ascending = true) const = 0; + // Modifies a vector of indices so that they refer to distinct values. + // If 'sort_order' is supplied, the indices will refer to values in sort order, + // otherwise the indices will be in original order. + virtual void distinct(std::vector& indices, util::Optional sort_order = util::none) const = 0; + + bool is_empty() const + { + return size() == 0; + } + ObjKey get_key() const + { + return m_const_obj->get_key(); + } + bool is_attached() const + { + return m_const_obj->is_valid(); + } + bool has_changed() const + { + update_if_needed(); + if (m_last_content_version != m_content_version) { + m_last_content_version = m_content_version; + return true; + } + return false; + } + ConstTableRef get_table() const + { + return m_const_obj->get_table(); + } + ColKey get_col_key() const + { + return m_col_key; + } + + bool operator==(const ConstLstBase& other) const + { + return get_key() == other.get_key() && get_col_key() == other.get_col_key(); + } + +protected: + template + friend class LstIterator; + friend class Transaction; + + + const ConstObj* m_const_obj; + ColKey m_col_key; + bool m_nullable = false; + + mutable std::vector m_deleted; + mutable uint_fast64_t m_content_version = 0; + mutable uint_fast64_t m_last_content_version = 0; + + ConstLstBase(ColKey col_key, const ConstObj* obj); + virtual bool init_from_parent() const = 0; + + ref_type get_child_ref(size_t) const noexcept override; + std::pair get_to_dot_parent(size_t) const override; + + void update_if_needed() const + { + auto content_version = m_const_obj->get_alloc().get_content_version(); + if (m_const_obj->update_if_needed() || content_version != m_content_version) { + init_from_parent(); + } + } + void update_content_version() const + { + m_content_version = m_const_obj->get_alloc().get_content_version(); + } + // Increase index by one. I we land on and index that is deleted, keep + // increasing until we get to a valid entry. + size_t incr(size_t ndx) const + { + ndx++; + if (!m_deleted.empty()) { + auto it = m_deleted.begin(); + auto end = m_deleted.end(); + while (it != end && *it < ndx) { + ++it; + } + // If entry is deleted, increase further + while (it != end && *it == ndx) { + ++it; + ++ndx; + } + } + return ndx; + } + // Convert from virtual to real index + size_t adjust(size_t ndx) const + { + if (!m_deleted.empty()) { + // Optimized for the case where the iterator is past that last deleted entry + auto it = m_deleted.rbegin(); + auto end = m_deleted.rend(); + while (it != end && *it >= ndx) { + if (*it == ndx) { + throw std::out_of_range("Element was deleted"); + } + ++it; + } + auto diff = end - it; + ndx -= diff; + } + return ndx; + } + void adj_remove(size_t ndx) + { + auto it = m_deleted.begin(); + auto end = m_deleted.end(); + while (it != end && *it <= ndx) { + ++ndx; + ++it; + } + m_deleted.insert(it, ndx); + } + void erase_repl(Replication* repl, size_t ndx) const; + void move_repl(Replication* repl, size_t from, size_t to) const; + void swap_repl(Replication* repl, size_t ndx1, size_t ndx2) const; + void clear_repl(Replication* repl) const; +}; + +/* + * This class implements a forward iterator over the elements in a Lst. + * + * The iterator is stable against deletions in the list. If you try to + * dereference an iterator that points to an element, that is deleted, the + * call will throw. + * + * Values are read into a member variable (m_val). This is the only way to + * implement operator-> and operator* returning a pointer and a reference resp. + * There is no overhead compared to the alternative where operator* would have + * to return T by value. + */ +template +class LstIterator { +public: + typedef std::forward_iterator_tag iterator_category; + typedef const T value_type; + typedef ptrdiff_t difference_type; + typedef const T* pointer; + typedef const T& reference; + + LstIterator(const ConstLstIf* l, size_t ndx) + : m_list(l) + , m_ndx(ndx) + { + } + pointer operator->() + { + m_val = m_list->get(m_list->adjust(m_ndx)); + return &m_val; + } + reference operator*() + { + return *operator->(); + } + LstIterator& operator++() + { + m_ndx = m_list->incr(m_ndx); + return *this; + } + LstIterator operator++(int) + { + LstIterator tmp(*this); + operator++(); + return tmp; + } + + bool operator!=(const LstIterator& rhs) + { + return m_ndx != rhs.m_ndx; + } + + bool operator==(const LstIterator& rhs) + { + return m_ndx == rhs.m_ndx; + } + +private: + friend class Lst; + T m_val; + const ConstLstIf* m_list; + size_t m_ndx; +}; + +template +inline void check_column_type(ColKey col) +{ + if (col && col.get_type() != ColumnTypeTraits::column_id) { + throw LogicError(LogicError::list_type_mismatch); + } +} + +template <> +inline void check_column_type(ColKey col) +{ + if (col && (col.get_type() != col_type_Int || col.get_attrs().test(col_attr_Nullable))) { + throw LogicError(LogicError::list_type_mismatch); + } +} + +template <> +inline void check_column_type>(ColKey col) +{ + if (col && (col.get_type() != col_type_Int || !col.get_attrs().test(col_attr_Nullable))) { + throw LogicError(LogicError::list_type_mismatch); + } +} + +template <> +inline void check_column_type(ColKey col) +{ + if (col && col.get_type() != col_type_LinkList) { + throw LogicError(LogicError::list_type_mismatch); + } +} + +/// This class defines the interface to ConstList, except for the constructor +/// The ConstList class has the ConstObj member m_obj, which should not be +/// inherited from Lst. +template +class ConstLstIf : public virtual ConstLstBase { +public: + /** + * Only member functions not referring to an index in the list will check if + * the object is up-to-date. The logic is that the user must always check the + * size before referring to a particular index, and size() will check for update. + */ + size_t size() const override + { + if (!is_attached()) + return 0; + update_if_needed(); + if (!m_valid) + return 0; + + return m_tree->size(); + } + bool is_null(size_t ndx) const final + { + return m_nullable && get(ndx) == BPlusTree::default_value(true); + } + Mixed get_any(size_t ndx) const final + { + return Mixed(get(ndx)); + } + + Mixed min(size_t* return_ndx = nullptr) const final; + Mixed max(size_t* return_ndx = nullptr) const final; + Mixed sum(size_t* return_cnt = nullptr) const final; + Mixed avg(size_t* return_cnt = nullptr) const final; + + void sort(std::vector& indices, bool ascending = true) const final; + void distinct(std::vector& indices, util::Optional sort_order = util::none) const final; + + T get(size_t ndx) const + { + if (ndx >= size()) { + throw std::out_of_range("Index out of range"); + } + return m_tree->get(ndx); + } + T operator[](size_t ndx) const + { + return get(ndx); + } + LstIterator begin() const + { + return LstIterator(this, 0); + } + LstIterator end() const + { + return LstIterator(this, size() + m_deleted.size()); + } + size_t find_first(T value) const + { + if (!m_valid && !init_from_parent()) + return not_found; + return m_tree->find_first(value); + } + template + void find_all(T value, Func&& func) const + { + if (m_valid && init_from_parent()) + m_tree->find_all(value, std::forward(func)); + } + const BPlusTree& get_tree() const + { + return *m_tree; + } + +protected: + mutable std::unique_ptr> m_tree; + mutable bool m_valid = false; + + ConstLstIf() + : ConstLstBase(ColKey{}, nullptr) + { + } + + ConstLstIf(Allocator& alloc) + : ConstLstBase(ColKey{}, nullptr) + , m_tree(new BPlusTree(alloc)) + { + check_column_type(m_col_key); + + m_tree->set_parent(this, 0); // ndx not used, implicit in m_owner + } + + ConstLstIf(const ConstLstIf& other) + : ConstLstBase(other.m_col_key, nullptr) + , m_valid(other.m_valid) + { + if (other.m_tree) { + Allocator& alloc = other.m_tree->get_alloc(); + m_tree = std::make_unique>(alloc); + m_tree->set_parent(this, 0); + if (m_valid) + m_tree->init_from_ref(other.m_tree->get_ref()); + } + } + + ConstLstIf(ConstLstIf&& other) noexcept + : ConstLstBase(ColKey{}, nullptr) + , m_tree(std::move(other.m_tree)) + , m_valid(other.m_valid) + { + m_tree->set_parent(this, 0); + } + + ConstLstIf& operator=(const ConstLstIf& other) + { + if (this != &other) { + m_valid = other.m_valid; + m_col_key = other.m_col_key; + m_deleted.clear(); + m_tree = nullptr; + + if (other.m_tree) { + Allocator& alloc = other.m_tree->get_alloc(); + m_tree = std::make_unique>(alloc); + m_tree->set_parent(this, 0); + if (m_valid) + m_tree->init_from_ref(other.m_tree->get_ref()); + } + } + return *this; + } + + bool init_from_parent() const final + { + m_valid = m_tree->init_from_parent(); + update_content_version(); + return m_valid; + } +}; + +template +class ConstLst : public ConstLstIf { +public: + ConstLst(const ConstObj& owner, ColKey col_key); + ConstLst(ConstLst&& other) noexcept + : ConstLstBase(other.m_col_key, &m_obj) + , ConstLstIf(std::move(other)) + , m_obj(std::move(other.m_obj)) + { + ConstLstBase::m_nullable = other.ConstLstBase::m_nullable; + } + +private: + ConstObj m_obj; + void update_child_ref(size_t, ref_type) override + { + } +}; +/* + * This class defines a virtual interface to a writable list + */ +class LstBase : public virtual ConstLstBase { +public: + LstBase() + : ConstLstBase(ColKey{}, nullptr) + { + } + virtual ~LstBase() + { + } + auto clone() const + { + return static_cast(m_const_obj)->get_listbase_ptr(m_col_key); + } + virtual void set_null(size_t ndx) = 0; + virtual void insert_null(size_t ndx) = 0; + virtual void insert_any(size_t ndx, Mixed val) = 0; + virtual void resize(size_t new_size) = 0; + virtual void remove(size_t from, size_t to) = 0; + virtual void move(size_t from, size_t to) = 0; + virtual void swap(size_t ndx1, size_t ndx2) = 0; + virtual void clear() = 0; +}; + +template +class Lst : public ConstLstIf, public LstBase { +public: + using ConstLstIf::m_tree; + using ConstLstIf::get; + + Lst() + : ConstLstBase({}, &m_obj) + { + } + Lst(const Obj& owner, ColKey col_key); + Lst(const Lst& other); + Lst(Lst&& other) noexcept; + + Lst& operator=(const Lst& other); + Lst& operator=(const BPlusTree& other); + + void update_child_ref(size_t, ref_type new_ref) override + { + m_obj.set_int(ConstLstBase::m_col_key, from_ref(new_ref)); + } + + void create() + { + m_tree->create(); + ConstLstIf::m_valid = true; + } + + void set_null(size_t ndx) override + { + set(ndx, BPlusTree::default_value(m_nullable)); + } + + void insert_null(size_t ndx) override + { + insert(ndx, BPlusTree::default_value(m_nullable)); + } + + void insert_any(size_t ndx, Mixed val) override + { + if (val.is_null()) { + insert_null(ndx); + } + else { + insert(ndx, val.get::type>()); + } + } + + void resize(size_t new_size) override + { + update_if_needed(); + size_t current_size = m_tree->size(); + while (new_size > current_size) { + insert_null(current_size++); + } + remove(new_size, current_size); + m_obj.bump_both_versions(); + } + + void add(T value) + { + insert(size(), value); + } + + T set(size_t ndx, T value) + { + REALM_ASSERT_DEBUG(!update_if_needed()); + + if (value_is_null(value) && !m_nullable) + throw LogicError(LogicError::column_not_nullable); + + // get will check for ndx out of bounds + T old = get(ndx); + if (old != value) { + ensure_writeable(); + do_set(ndx, value); + m_obj.bump_content_version(); + } + if (Replication* repl = this->m_const_obj->get_replication()) { + set_repl(repl, ndx, value); + } + return old; + } + + void insert(size_t ndx, T value) + { + REALM_ASSERT_DEBUG(!update_if_needed()); + + if (value_is_null(value) && !m_nullable) + throw LogicError(LogicError::column_not_nullable); + + ensure_created(); + if (ndx > m_tree->size()) { + throw std::out_of_range("Index out of range"); + } + ensure_writeable(); + if (Replication* repl = this->m_const_obj->get_replication()) { + insert_repl(repl, ndx, value); + } + do_insert(ndx, value); + m_obj.bump_content_version(); + } + + T remove(LstIterator& it) + { + return remove(ConstLstBase::adjust(it.m_ndx)); + } + + T remove(size_t ndx) + { + REALM_ASSERT_DEBUG(!update_if_needed()); + ensure_writeable(); + if (Replication* repl = this->m_const_obj->get_replication()) { + ConstLstBase::erase_repl(repl, ndx); + } + T old = get(ndx); + do_remove(ndx); + ConstLstBase::adj_remove(ndx); + m_obj.bump_content_version(); + + return old; + } + + void remove(size_t from, size_t to) override + { + while (from < to) { + remove(--to); + } + } + + void move(size_t from, size_t to) override + { + REALM_ASSERT_DEBUG(!update_if_needed()); + if (from != to) { + ensure_writeable(); + if (Replication* repl = this->m_const_obj->get_replication()) { + ConstLstBase::move_repl(repl, from, to); + } + if (to > from) { + to++; + } + else { + from++; + } + // We use swap here as it handles the special case for StringData where + // 'to' and 'from' points into the same array. In this case you cannot + // set an entry with the result of a get from another entry in the same + // leaf. + m_tree->insert(to, BPlusTree::default_value(m_nullable)); + m_tree->swap(from, to); + m_tree->erase(from); + + m_obj.bump_content_version(); + } + } + + void swap(size_t ndx1, size_t ndx2) override + { + REALM_ASSERT_DEBUG(!update_if_needed()); + if (ndx1 != ndx2) { + if (Replication* repl = this->m_const_obj->get_replication()) { + ConstLstBase::swap_repl(repl, ndx1, ndx2); + } + m_tree->swap(ndx1, ndx2); + m_obj.bump_content_version(); + } + } + + void clear() override + { + ensure_created(); + update_if_needed(); + ensure_writeable(); + if (size() > 0) { + if (Replication* repl = this->m_const_obj->get_replication()) { + ConstLstBase::clear_repl(repl); + } + m_tree->clear(); + m_obj.bump_content_version(); + } + } + +protected: + Obj m_obj; + bool update_if_needed() + { + if (m_obj.update_if_needed()) { + return init_from_parent(); + } + return false; + } + void ensure_created() + { + if (!ConstLstIf::m_valid && m_obj.is_valid()) { + create(); + } + } + void ensure_writeable() + { + if (m_obj.ensure_writeable()) { + init_from_parent(); + } + } + void do_set(size_t ndx, T value) + { + m_tree->set(ndx, value); + } + void do_insert(size_t ndx, T value) + { + m_tree->insert(ndx, value); + } + void do_remove(size_t ndx) + { + m_tree->erase(ndx); + } + void set_repl(Replication* repl, size_t ndx, T value); + void insert_repl(Replication* repl, size_t ndx, T value); +}; + +template +Lst::Lst(const Lst& other) + : ConstLstBase(other.m_col_key, &m_obj) + , ConstLstIf(other) + , m_obj(other.m_obj) +{ + m_nullable = other.m_nullable; +} + +template +Lst::Lst(Lst&& other) noexcept + : ConstLstBase(other.m_col_key, &m_obj) + , ConstLstIf(std::move(other)) + , m_obj(std::move(other.m_obj)) +{ + m_nullable = other.m_nullable; +} + +template +Lst& Lst::operator=(const Lst& other) +{ + ConstLstIf::operator=(other); + m_obj = other.m_obj; + m_nullable = other.m_nullable; + return *this; +} + +template +Lst& Lst::operator=(const BPlusTree& other) +{ + *m_tree = other; + return *this; +} + + +template <> +void Lst::do_set(size_t ndx, ObjKey target_key); + +template <> +void Lst::do_insert(size_t ndx, ObjKey target_key); + +template <> +void Lst::do_remove(size_t ndx); + +template <> +void Lst::clear(); + +class ConstLnkLst : public ConstLstIf { +public: + ConstLnkLst() + : ConstLstBase({}, &m_obj) + { + } + + ConstLnkLst(const ConstObj& obj, ColKey col_key) + : ConstLstBase(col_key, &m_obj) + , ConstLstIf(obj.get_alloc()) + , m_obj(obj) + { + this->init_from_parent(); + } + ConstLnkLst(ConstLnkLst&& other) noexcept + : ConstLstBase(other.m_col_key, &m_obj) + , ConstLstIf(std::move(other)) + , m_obj(std::move(other.m_obj)) + { + } + + // Getting links + ConstObj operator[](size_t link_ndx) const + { + return get_object(link_ndx); + } + ConstObj get_object(size_t link_ndx) const; + + void update_child_ref(size_t, ref_type) override + { + } + +private: + ConstObj m_obj; +}; + +class LnkLst : public Lst, public ObjList { +public: + LnkLst() + : ConstLstBase({}, &m_obj) + , ObjList(this->m_tree.get()) + { + } + LnkLst(const Obj& owner, ColKey col_key); + LnkLst(const LnkLst& other) + : ConstLstBase(other.m_col_key, &m_obj) + , Lst(other) + , ObjList(this->m_tree.get(), m_obj.get_target_table(m_col_key)) + { + } + LnkLst(LnkLst&& other) noexcept + : ConstLstBase(other.m_col_key, &m_obj) + , Lst(std::move(other)) + , ObjList(this->m_tree.get(), m_obj.get_target_table(m_col_key)) + { + } + LnkLst& operator=(const LnkLst& other) + { + Lst::operator=(other); + this->ObjList::assign(this->m_tree.get(), m_obj.get_target_table(m_col_key)); + return *this; + } + + LnkLstPtr clone() const + { + if (m_obj.is_valid()) { + return std::make_unique(m_obj, m_col_key); + } + else { + return std::make_unique(); + } + } + TableRef get_target_table() const + { + return m_table.cast_away_const(); + } + bool is_in_sync() const override + { + return true; + } + size_t size() const override + { + return Lst::size(); + } + + Obj get_object(size_t ndx); + + Obj operator[](size_t ndx) + { + return get_object(ndx); + } + + using Lst::find_first; + using Lst::find_all; + + TableView get_sorted_view(SortDescriptor order) const; + TableView get_sorted_view(ColKey column_key, bool ascending = true) const; + void remove_target_row(size_t link_ndx); + void remove_all_target_rows(); + +private: + friend class DB; + friend class ConstTableView; + friend class Query; + void get_dependencies(TableVersions&) const override; + void sync_if_needed() const override; +}; + +template +ConstLst ConstObj::get_list(ColKey col_key) const +{ + return ConstLst(*this, col_key); +} + +template +ConstLstPtr ConstObj::get_list_ptr(ColKey col_key) const +{ + Obj obj(*this); + return std::const_pointer_cast>(obj.get_list_ptr(col_key)); +} + +template +Lst Obj::get_list(ColKey col_key) const +{ + return Lst(*this, col_key); +} + +template +LstPtr Obj::get_list_ptr(ColKey col_key) const +{ + return std::make_unique>(*this, col_key); +} + +template <> +inline LstPtr Obj::get_list_ptr(ColKey col_key) const +{ + return get_linklist_ptr(col_key); +} + +inline ConstLnkLst ConstObj::get_linklist(ColKey col_key) const +{ + return ConstLnkLst(*this, col_key); +} + +inline ConstLnkLst ConstObj::get_linklist(StringData col_name) const +{ + return get_linklist(get_column_key(col_name)); +} + +inline ConstLnkLstPtr ConstObj::get_linklist_ptr(ColKey col_key) const +{ + Obj obj(*this); + return obj.get_linklist_ptr(col_key); +} + +inline LnkLst Obj::get_linklist(ColKey col_key) const +{ + return LnkLst(*this, col_key); +} + +inline LnkLstPtr Obj::get_linklist_ptr(ColKey col_key) const +{ + return std::make_unique(*this, col_key); +} + +inline LnkLst Obj::get_linklist(StringData col_name) const +{ + return get_linklist(get_column_key(col_name)); +} + +template +inline typename ColumnTypeTraits::sum_type list_sum(const ConstLstIf& list, size_t* return_cnt = nullptr) +{ + return bptree_sum(list.get_tree(), return_cnt); +} + +template +inline typename ColumnTypeTraits::minmax_type list_maximum(const ConstLstIf& list, size_t* return_ndx = nullptr) +{ + return bptree_maximum(list.get_tree(), return_ndx); +} + +template +inline typename ColumnTypeTraits::minmax_type list_minimum(const ConstLstIf& list, size_t* return_ndx = nullptr) +{ + return bptree_minimum(list.get_tree(), return_ndx); +} + +template +inline double list_average(const ConstLstIf& list, size_t* return_cnt = nullptr) +{ + return bptree_average(list.get_tree(), return_cnt); +} +} + +#endif /* REALM_LIST_HPP */ diff --git a/src/vendor-include/realm-ios/include/realm/metrics/metric_timer.hpp b/src/vendor-include/realm-ios/include/realm/metrics/metric_timer.hpp new file mode 100644 index 000000000..b80c6f549 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/metrics/metric_timer.hpp @@ -0,0 +1,100 @@ +/************************************************************************* + * + * 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_METRIC_TIMER_HPP +#define REALM_METRIC_TIMER_HPP + +#include + +#include +#include +#include + +namespace realm { +namespace metrics { + +using nanosecond_storage_t = int64_t; + +class MetricTimerResult { +public: + MetricTimerResult(); + ~MetricTimerResult(); + nanosecond_storage_t get_elapsed_nanoseconds() const; + void report_nanoseconds(nanosecond_storage_t time); + +protected: + nanosecond_storage_t m_elapsed_nanoseconds; +}; + + +class MetricTimer { +public: + MetricTimer(std::shared_ptr destination = nullptr); + ~MetricTimer(); + + void reset(); + + /// Returns elapsed time in nanoseconds since last call to reset(). + nanosecond_storage_t get_elapsed_nanoseconds() const; + /// Same as get_elapsed_time(). + operator nanosecond_storage_t() const; + + /// Format the elapsed time on the form 0h00m, 00m00s, 00.00s, or + /// 000.0ms depending on magnitude. + static void format(nanosecond_storage_t nanoseconds, std::ostream&); + + static std::string format(nanosecond_storage_t nanoseconds); + +private: + using clock_type = std::chrono::high_resolution_clock; + using time_point = std::chrono::time_point; + time_point m_start; + time_point m_paused_at; + std::shared_ptr m_dest; + + time_point get_timer_ticks() const; + nanosecond_storage_t calc_elapsed_nanoseconds(time_point begin, time_point end) const; +}; + + +inline void MetricTimer::reset() +{ + m_start = get_timer_ticks(); +} + +inline nanosecond_storage_t MetricTimer::get_elapsed_nanoseconds() const +{ + return calc_elapsed_nanoseconds(m_start, get_timer_ticks()); +} + +inline MetricTimer::operator nanosecond_storage_t() const +{ + return get_elapsed_nanoseconds(); +} + +inline std::ostream& operator<<(std::ostream& out, const MetricTimer& timer) +{ + MetricTimer::format(timer, out); + return out; +} + + +} // namespace metrics +} // namespace realm + +#endif // REALM_METRIC_TIMER_HPP diff --git a/src/vendor-include/realm-ios/include/realm/metrics/metrics.hpp b/src/vendor-include/realm-ios/include/realm/metrics/metrics.hpp new file mode 100644 index 000000000..3e9ff00a9 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/metrics/metrics.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_METRICS_HPP +#define REALM_METRICS_HPP + +#include + +#include +#include +#include +#include "realm/util/fixed_size_buffer.hpp" + +namespace realm { + +class Group; + +namespace metrics { + +class Metrics { +public: + Metrics(size_t max_history_size); + ~Metrics() noexcept; + size_t num_query_metrics() const; + size_t num_transaction_metrics() const; + + void add_query(QueryInfo info); + void add_transaction(TransactionInfo info); + + void start_read_transaction(); + void start_write_transaction(); + void end_read_transaction(size_t total_size, size_t free_space, size_t num_objects, size_t num_versions, + size_t num_decrypted_pages); + void end_write_transaction(size_t total_size, size_t free_space, size_t num_objects, size_t num_versions, + size_t num_decrypted_pages); + static std::unique_ptr report_fsync_time(const Group& g); + static std::unique_ptr report_write_time(const Group& g); + + using QueryInfoList = util::FixedSizeBuffer; + using TransactionInfoList = util::FixedSizeBuffer; + + // Get the list of metric objects tracked since the last take + std::unique_ptr take_queries(); + std::unique_ptr take_transactions(); + +private: + std::unique_ptr m_query_info; + std::unique_ptr m_transaction_info; + + std::unique_ptr m_pending_read; + std::unique_ptr m_pending_write; + + size_t m_max_num_queries; + size_t m_max_num_transactions; +}; + +} // namespace metrics +} // namespace realm + + +#endif // REALM_METRICS_HPP diff --git a/src/vendor-include/realm-ios/include/realm/metrics/query_info.hpp b/src/vendor-include/realm-ios/include/realm/metrics/query_info.hpp new file mode 100644 index 000000000..7bc9795db --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/metrics/query_info.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_QUERY_INFO_HPP +#define REALM_QUERY_INFO_HPP + +#include +#include +#include + +#include +#include +#include + +namespace realm { + +class Query; // forward declare in namespace realm + +namespace metrics { + +class QueryInfo { +public: + enum QueryType { + type_Find, + type_FindAll, + type_Count, + type_Sum, + type_Average, + type_Maximum, + type_Minimum, + type_Invalid + }; + + QueryInfo(const Query* query, QueryType type); + ~QueryInfo() noexcept; + + std::string get_description() const; + std::string get_table_name() const; + QueryType get_type() const; + nanosecond_storage_t get_query_time_nanoseconds() const; + + static std::unique_ptr track(const Query* query, QueryType type); + static QueryType type_from_action(Action action); + +private: + std::string m_description; + std::string m_table_name; + QueryType m_type; + std::shared_ptr m_query_time; +}; + +} // namespace metrics +} // namespace realm + +#endif // REALM_QUERY_INFO_HPP diff --git a/src/vendor-include/realm-ios/include/realm/metrics/transaction_info.hpp b/src/vendor-include/realm-ios/include/realm/metrics/transaction_info.hpp new file mode 100644 index 000000000..2706024a1 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/metrics/transaction_info.hpp @@ -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. + * + **************************************************************************/ + +#ifndef REALM_TRANSACTION_INFO_HPP +#define REALM_TRANSACTION_INFO_HPP + +#include +#include + +#include +#include + +namespace realm { +namespace metrics { + +class Metrics; + +class TransactionInfo { +public: + enum TransactionType { read_transaction, write_transaction }; + TransactionInfo(TransactionType type); + TransactionInfo(const TransactionInfo&) = default; + ~TransactionInfo() noexcept; + + TransactionType get_transaction_type() const; + // the transaction time is a total amount which includes fsync_time + write_time + user_time + nanosecond_storage_t get_transaction_time_nanoseconds() const; + nanosecond_storage_t get_fsync_time_nanoseconds() const; + nanosecond_storage_t get_write_time_nanoseconds() const; + size_t get_disk_size() const; + size_t get_free_space() const; + size_t get_total_objects() const; + size_t get_num_available_versions() const; + size_t get_num_decrypted_pages() const; + +private: + MetricTimerResult m_transaction_time; + std::shared_ptr m_fsync_time; + std::shared_ptr m_write_time; + MetricTimer m_transact_timer; + + size_t m_realm_disk_size; + size_t m_realm_free_space; + size_t m_total_objects; + TransactionType m_type; + size_t m_num_versions; + size_t m_num_decrypted_pages; + + friend class Metrics; + void update_stats(size_t disk_size, size_t free_space, size_t total_objects, size_t available_versions, + size_t num_decrypted_pages); + void finish_timer(); +}; + +} // namespace metrics +} // namespace realm + +#endif // REALM_TRANSACTION_INFO_HPP diff --git a/src/vendor-include/realm-ios/include/realm/mixed.hpp b/src/vendor-include/realm-ios/include/realm/mixed.hpp new file mode 100644 index 000000000..7584f469c --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/mixed.hpp @@ -0,0 +1,413 @@ +/************************************************************************* + * + * 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() to extract that value. You +/// may call get_type() to discover what type of value is currently +/// stored. Calling get() 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 + : m_type(0) + { + } + + Mixed(util::None) noexcept + : Mixed() + { + } + + Mixed(int i) noexcept + : Mixed(int64_t(i)) + { + } + Mixed(int64_t) noexcept; + Mixed(bool) noexcept; + Mixed(float) noexcept; + Mixed(double) noexcept; + Mixed(util::Optional) noexcept; + Mixed(util::Optional) noexcept; + Mixed(util::Optional) noexcept; + Mixed(util::Optional) noexcept; + Mixed(StringData) noexcept; + Mixed(BinaryData) noexcept; + Mixed(Timestamp) noexcept; + Mixed(ObjKey) 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 + : Mixed(StringData(c_str)) + { + } + Mixed(const char* c_str) noexcept + : Mixed(StringData(c_str)) + { + } + Mixed(const std::string& s) noexcept + : Mixed(StringData(s)) + { + } + + ~Mixed() noexcept + { + } + + DataType get_type() const noexcept + { + REALM_ASSERT(m_type); + return DataType(m_type - 1); + } + + template + T get() const noexcept; + + // These functions are kept to be backwards compatible + int64_t get_int() const; + bool get_bool() const; + float get_float() const; + double get_double() const; + StringData get_string() const; + BinaryData get_binary() const; + Timestamp get_timestamp() const; + + bool is_null() const; + int compare(const Mixed& b) const; + bool operator==(const Mixed& other) const + { + return compare(other) == 0; + } + bool operator!=(const Mixed& other) const + { + return compare(other) != 0; + } + +private: + friend std::ostream& operator<<(std::ostream& out, const Mixed& m); + + uint32_t m_type; + union { + int32_t short_val; + uint32_t ushort_val; + }; + + union { + int64_t int_val; + bool bool_val; + float float_val; + double double_val; + const char* str_val; + }; +}; + +// Implementation: + +inline Mixed::Mixed(int64_t v) noexcept +{ + m_type = type_Int + 1; + int_val = v; +} + +inline Mixed::Mixed(bool v) noexcept +{ + m_type = type_Bool + 1; + bool_val = v; +} + +inline Mixed::Mixed(float v) noexcept +{ + m_type = type_Float + 1; + float_val = v; +} + +inline Mixed::Mixed(double v) noexcept +{ + m_type = type_Double + 1; + double_val = v; +} + +inline Mixed::Mixed(util::Optional v) noexcept +{ + if (v) { + m_type = type_Int + 1; + int_val = *v; + } + else { + m_type = 0; + } +} + +inline Mixed::Mixed(util::Optional v) noexcept +{ + if (v) { + m_type = type_Bool + 1; + bool_val = *v; + } + else { + m_type = 0; + } +} + +inline Mixed::Mixed(util::Optional v) noexcept +{ + if (v) { + m_type = type_Float + 1; + float_val = *v; + } + else { + m_type = 0; + } +} + +inline Mixed::Mixed(util::Optional v) noexcept +{ + if (v) { + m_type = type_Double + 1; + double_val = *v; + } + else { + m_type = 0; + } +} + +inline Mixed::Mixed(StringData v) noexcept +{ + if (!v.is_null()) { + m_type = type_String + 1; + str_val = v.data(); + ushort_val = uint32_t(v.size()); + } + else { + m_type = 0; + } +} + +inline Mixed::Mixed(BinaryData v) noexcept +{ + if (!v.is_null()) { + m_type = type_Binary + 1; + str_val = v.data(); + ushort_val = uint32_t(v.size()); + } + else { + m_type = 0; + } +} + +inline Mixed::Mixed(Timestamp v) noexcept +{ + if (!v.is_null()) { + m_type = type_Timestamp + 1; + int_val = v.get_seconds(); + short_val = v.get_nanoseconds(); + } + else { + m_type = 0; + } +} + +inline Mixed::Mixed(ObjKey v) noexcept +{ + if (v) { + m_type = type_Link + 1; + int_val = v.value; + } + else { + m_type = 0; + } +} + +template <> +inline int64_t Mixed::get() const noexcept +{ + REALM_ASSERT(get_type() == type_Int); + return int_val; +} + +inline int64_t Mixed::get_int() const +{ + return get(); +} + +template <> +inline bool Mixed::get() const noexcept +{ + REALM_ASSERT(get_type() == type_Bool); + return bool_val; +} + +inline bool Mixed::get_bool() const +{ + return get(); +} + +template <> +inline float Mixed::get() const noexcept +{ + REALM_ASSERT(get_type() == type_Float); + return float_val; +} + +inline float Mixed::get_float() const +{ + return get(); +} + +template <> +inline double Mixed::get() const noexcept +{ + REALM_ASSERT(get_type() == type_Double); + return double_val; +} + +inline double Mixed::get_double() const +{ + return get(); +} + +template <> +inline StringData Mixed::get() const noexcept +{ + REALM_ASSERT(get_type() == type_String); + return StringData(str_val, ushort_val); +} + +inline StringData Mixed::get_string() const +{ + return get(); +} + +template <> +inline BinaryData Mixed::get() const noexcept +{ + REALM_ASSERT(get_type() == type_Binary); + return BinaryData(str_val, ushort_val); +} + +inline BinaryData Mixed::get_binary() const +{ + return get(); +} + +template <> +inline Timestamp Mixed::get() const noexcept +{ + REALM_ASSERT(get_type() == type_Timestamp); + return Timestamp(int_val, short_val); +} + +inline Timestamp Mixed::get_timestamp() const +{ + return get(); +} + +template <> +inline ObjKey Mixed::get() const noexcept +{ + REALM_ASSERT(get_type() == type_Link); + return ObjKey(int_val); +} + +inline bool Mixed::is_null() const +{ + return (m_type == 0); +} + +std::ostream& operator<<(std::ostream& out, const Mixed& m); + +} // namespace realm + +#endif // REALM_MIXED_HPP diff --git a/src/vendor-include/realm-ios/include/realm/node.hpp b/src/vendor-include/realm-ios/include/realm/node.hpp new file mode 100644 index 000000000..7c0f08c96 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/node.hpp @@ -0,0 +1,358 @@ +/************************************************************************* + * + * Copyright 2018 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_NODE_HPP +#define REALM_NODE_HPP + +#include +#include + +namespace realm { + +/// 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); + +/// Alias for realm::npos. +const size_t not_found = npos; + +/// All accessor classes that logically contains other objects must inherit +/// this class. +/// +/// A database node accessor contains information about the parent of the +/// referenced node. This 'reverse' reference is not explicitly 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. +class ArrayParent { +public: + virtual ~ArrayParent() noexcept + { + } + + virtual ref_type get_child_ref(size_t child_ndx) const noexcept = 0; + virtual void update_child_ref(size_t child_ndx, ref_type new_ref) = 0; + // Used only by Array::to_dot(). + virtual std::pair get_to_dot_parent(size_t ndx_in_parent) const = 0; +}; + +/// 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 node 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 node accessor contains information about the parent of the referenced +/// node. This 'reverse' reference is not explicitly present in the +/// underlying node hierarchy, but it is needed when modifying a node. A +/// modification may lead to relocation of the underlying node, and the +/// parent must be updated accordingly. Since this applies recursively all the +/// way to the root node, it is essential that the entire chain of parent +/// accessors is constructed and properly maintained when a particular node 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 unaffected 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 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 node. 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 Node : public NodeHeader { +public: + // FIXME: Should not be public + char* m_data = nullptr; // Points to first byte after header + + /*********************** Constructor / destructor ************************/ + + // The object will not be fully initialized when using this constructor + explicit Node(Allocator& allocator) noexcept + : m_alloc(allocator) + { + } + + virtual ~Node() + { + } + + /**************************** Initializers *******************************/ + + /// Same as init_from_ref(ref_type) but avoid the mapping of 'ref' to memory + /// pointer. + char* init_from_mem(MemRef mem) noexcept + { + char* header = mem.get_addr(); + m_ref = mem.get_ref(); + m_data = get_data_from_header(header); + m_width = get_width_from_header(header); + m_size = get_size_from_header(header); + + return header; + } + + /************************** access functions *****************************/ + + bool is_attached() const noexcept + { + return m_data != nullptr; + } + + inline bool is_read_only() const noexcept + { + REALM_ASSERT_DEBUG(is_attached()); + return m_alloc.is_read_only(m_ref); + } + + size_t size() const noexcept + { + REALM_ASSERT_DEBUG(is_attached()); + return m_size; + } + + bool is_empty() const noexcept + { + return size() == 0; + } + + ref_type get_ref() const noexcept + { + return m_ref; + } + MemRef get_mem() const noexcept + { + return MemRef(get_header_from_data(m_data), m_ref, m_alloc); + } + Allocator& get_alloc() const noexcept + { + return m_alloc; + } + /// Get the address of the header of this array. + char* get_header() const noexcept + { + return get_header_from_data(m_data); + } + + bool has_parent() const noexcept + { + return m_parent != nullptr; + } + ArrayParent* get_parent() const noexcept + { + return m_parent; + } + size_t get_ndx_in_parent() const noexcept + { + return m_ndx_in_parent; + } + /// 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 + { + REALM_ASSERT_DEBUG(m_parent); + ref_type ref = m_parent->get_child_ref(m_ndx_in_parent); + return ref; + } + + /// The meaning of 'width' depends on the context in which this + /// array is used. + size_t get_width() const noexcept + { + return m_width; + } + + /***************************** modifiers *********************************/ + + /// Detach from the underlying array node. This method has no effect if the + /// accessor is currently unattached (idempotency). + void detach() noexcept + { + m_data = nullptr; + } + + /// 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 + { + if (!is_attached()) + return; + char* header = get_header_from_data(m_data); + m_alloc.free_(m_ref, header); + m_data = nullptr; + } + + /// Shorthand for `destroy(MemRef(ref, alloc), alloc)`. + static void destroy(ref_type ref, Allocator& alloc) noexcept + { + destroy(MemRef(ref, alloc), alloc); + } + + /// Destroy only the specified array node, not its children. See also + /// destroy_deep(MemRef, Allocator&). + static void destroy(MemRef mem, Allocator& alloc) noexcept + { + alloc.free_(mem); + } + + + /// 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 + { + m_parent = parent; + m_ndx_in_parent = ndx_in_parent; + } + void set_ndx_in_parent(size_t ndx) noexcept + { + m_ndx_in_parent = ndx; + } + + /// 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() + { + if (m_parent) + m_parent->update_child_ref(m_ndx_in_parent, m_ref); + } + +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; + + size_t m_ref; + Allocator& m_alloc; + size_t m_size = 0; // Number of elements currently stored. + uint_least8_t m_width = 0; // Size of an element (meaning depend on type of array). + +#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 + + void alloc(size_t init_size, size_t new_width); + void 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(); + } + } + + static MemRef create_node(size_t size, Allocator& alloc, bool context_flag = false, Type type = type_Normal, + WidthType width_type = wtype_Ignore, int width = 1); + + void set_header_size(size_t value) noexcept + { + set_size_in_header(value, get_header()); + } + + // 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; + 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; + +private: + ArrayParent* m_parent = nullptr; + size_t m_ndx_in_parent = 0; // Ignored if m_parent is null. + + void do_copy_on_write(size_t minimum_size = 0); +}; + +class Spec; + +/// Base class for all nodes holding user data +class ArrayPayload { +public: + virtual ~ArrayPayload(); + virtual void init_from_ref(ref_type) noexcept = 0; + virtual void set_parent(ArrayParent* parent, size_t ndx_in_parent) noexcept = 0; + virtual bool need_spec() const + { + return false; + } + virtual void set_spec(Spec*, size_t) const + { + } +}; + + +inline void Node::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_is_inner_bptree_node_in_header(is_inner_bptree_node, header); + set_hasrefs_in_header(has_refs, header); + set_context_flag_in_header(context_flag, header); + set_wtype_in_header(width_type, header); + set_width_in_header(width, header); + set_size_in_header(size, header); + set_capacity_in_header(capacity, header); +} +} + +#endif /* REALM_NODE_HPP */ diff --git a/src/vendor-include/realm-ios/include/realm/node_header.hpp b/src/vendor-include/realm-ios/include/realm/node_header.hpp new file mode 100644 index 000000000..be6fd0cff --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/node_header.hpp @@ -0,0 +1,241 @@ +/************************************************************************* + * + * Copyright 2018 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_NODE_HEADER_HPP +#define REALM_NODE_HEADER_HPP + +#include + +namespace realm { + +const size_t max_array_size = 0x00ffffffL; // Maximum number of elements in an array +const size_t max_array_payload_aligned = 0x07ffffc0L; // Maximum number of bytes that the payload of an array can be + +class NodeHeader { +public: + 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 + }; + + enum WidthType { + wtype_Bits = 0, // width indicates how many bits every element occupies + wtype_Multiply = 1, // width indicates how many bytes every element occupies + wtype_Ignore = 2, // each element is 1 byte + }; + + 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"); + + static char* get_data_from_header(char* header) noexcept + { + return header + header_size; + } + + static char* get_header_from_data(char* data) noexcept + { + return data - header_size; + } + + static const char* get_data_from_header(const char* header) noexcept + { + return get_data_from_header(const_cast(header)); + } + + static bool 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; + } + + static bool get_hasrefs_from_header(const char* header) noexcept + { + typedef unsigned char uchar; + const uchar* h = reinterpret_cast(header); + return (int(h[4]) & 0x40) != 0; + } + + static bool 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; + } + + static WidthType 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); + } + + static uint_least8_t 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); + } + + static size_t 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]; + } + + static size_t get_capacity_from_header(const char* header) noexcept + { + typedef unsigned char uchar; + const uchar* h = reinterpret_cast(header); + return (size_t(h[0]) << 19) + (size_t(h[1]) << 11) + (h[2] << 3); + } + + static Type 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; + } + + static void set_is_inner_bptree_node_in_header(bool value, char* header) noexcept + { + typedef unsigned char uchar; + uchar* h = reinterpret_cast(header); + h[4] = uchar((int(h[4]) & ~0x80) | int(value) << 7); + } + + static void set_hasrefs_in_header(bool value, char* header) noexcept + { + typedef unsigned char uchar; + uchar* h = reinterpret_cast(header); + h[4] = uchar((int(h[4]) & ~0x40) | int(value) << 6); + } + + static void set_context_flag_in_header(bool value, char* header) noexcept + { + typedef unsigned char uchar; + uchar* h = reinterpret_cast(header); + h[4] = uchar((int(h[4]) & ~0x20) | int(value) << 5); + } + + static void set_wtype_in_header(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); + } + + static void set_width_in_header(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); + } + + static void set_size_in_header(size_t value, char* header) noexcept + { + REALM_ASSERT_3(value, <=, max_array_size); + 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 + static void set_capacity_in_header(size_t value, char* header) noexcept + { + REALM_ASSERT_3(value, <=, (0xffffff << 3)); + typedef unsigned char uchar; + uchar* h = reinterpret_cast(header); + h[0] = uchar((value >> 19) & 0x000000FF); + h[1] = uchar((value >> 11) & 0x000000FF); + h[2] = uchar(value >> 3 & 0x000000FF); + } + + static size_t 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; + } + + static size_t 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; + } +}; +} + +#endif /* REALM_NODE_HEADER_HPP */ diff --git a/src/vendor-include/realm-ios/include/realm/null.hpp b/src/vendor-include/realm-ios/include/realm/null.hpp new file mode 100644 index 000000000..733e79800 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/null.hpp @@ -0,0 +1,172 @@ +/************************************************************************* + * + * 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; + } + } +}; + +template +OS& operator<<(OS& os, const null&) +{ + os << "(null)"; + return os; +} + +} // namespace realm + +#endif // REALM_NULL_HPP diff --git a/src/vendor-include/realm-ios/include/realm/obj.hpp b/src/vendor-include/realm-ios/include/realm/obj.hpp new file mode 100644 index 000000000..91165e24e --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/obj.hpp @@ -0,0 +1,438 @@ +/************************************************************************* + * + * 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_OBJ_HPP +#define REALM_OBJ_HPP + +#include +#include +#include +#include +#include + +#define REALM_CLUSTER_IF + +namespace realm { + +class Replication; +class TableView; +class ConstLstBase; +class LstBase; +struct GlobalKey; + +template +class ConstLstIf; + +template +class ConstLst; + +template +class Lst; +template +using LstPtr = std::unique_ptr>; +template +using ConstLstPtr = std::unique_ptr>; +using ConstLstBasePtr = std::unique_ptr; +using LstBasePtr = std::unique_ptr; + +class LnkLst; +class ConstLnkLst; +using LnkLstPtr = std::unique_ptr; +using ConstLnkLstPtr = std::unique_ptr; + +// 'Object' would have been a better name, but it clashes with a class in ObjectStore +class ConstObj { +public: + ConstObj() + : m_table(nullptr) + , m_row_ndx(size_t(-1)) + , m_storage_version(-1) + , m_valid(false) + { + } + ConstObj(ConstTableRef table, MemRef mem, ObjKey key, size_t row_ndx); + + Allocator& get_alloc() const; + + bool operator==(const ConstObj& other) const; + + ObjKey get_key() const + { + return m_key; + } + + GlobalKey get_object_id() const; + + ConstTableRef get_table() const + { + return m_table; + } + + Replication* get_replication() const; + + // Check if this object is default constructed + explicit operator bool() const + { + return m_table != nullptr; + } + + // Check if the object is still alive + bool is_valid() const; + // Will throw if object is not valid + void check_valid() const; + // Delete object from table. Object is invalid afterwards. + void remove(); + + template + U get(ColKey col_key) const; + + Mixed get_any(ColKey col_key) const; + + template + U get(StringData col_name) const + { + return get(get_column_key(col_name)); + } + ConstObj get_linked_object(ColKey link_col_key) const; + int cmp(const ConstObj& other, ColKey col_key) const; + + template + ConstLst get_list(ColKey col_key) const; + template + ConstLstPtr get_list_ptr(ColKey col_key) const; + template + ConstLst get_list(StringData col_name) const + { + return get_list(get_column_key(col_name)); + } + + ConstLnkLst get_linklist(ColKey col_key) const; + ConstLnkLstPtr get_linklist_ptr(ColKey col_key) const; + ConstLnkLst get_linklist(StringData col_name) const; + + ConstLstBasePtr get_listbase_ptr(ColKey col_key) const; + + size_t get_link_count(ColKey col_key) const; + + bool is_null(ColKey col_key) const; + bool is_null(StringData col_name) const + { + return is_null(get_column_key(col_name)); + } + bool has_backlinks(bool only_strong_links) const; + size_t get_backlink_count(bool only_strong_links = false) const; + size_t get_backlink_count(const Table& origin, ColKey origin_col_key) const; + ObjKey get_backlink(const Table& origin, ColKey origin_col_key, size_t backlink_ndx) const; + TableView get_backlink_view(TableRef src_table, ColKey src_col_key); + + // To be used by the query system when a single object should + // be tested. Will allow a function to be called in the context + // of the owning cluster. + template + bool evaluate(T func) const + { + Cluster cluster(0, get_alloc(), *get_tree_top()); + cluster.init(m_mem); + cluster.set_offset(m_key.value - cluster.get_key_value(m_row_ndx)); + return func(&cluster, m_row_ndx); + } + + void to_json(std::ostream& out, size_t link_depth, std::map& renames, + std::vector& followed) const; + void to_json(std::ostream& out, size_t link_depth = 0, + std::map* renames = nullptr) const + { + std::map renames2; + renames = renames ? renames : &renames2; + + std::vector followed; + to_json(out, link_depth, *renames, followed); + } + + std::string to_string() const; + +protected: + friend class Obj; + friend class ColumnListBase; + friend class ConstLstBase; + friend class ConstLnkLst; + friend class LnkLst; + friend class LinkMap; + friend class ConstTableView; + friend class Transaction; + friend struct ClusterNode::IteratorState; + + mutable ConstTableRef m_table; + ObjKey m_key; + mutable MemRef m_mem; + mutable size_t m_row_ndx; + mutable uint64_t m_storage_version; + mutable bool m_valid; + + Allocator& _get_alloc() const; + bool update() const; + // update if needed - with and without check of table instance version: + bool update_if_needed() const; + bool _update_if_needed() const; // no check, use only when already checked + template + bool do_is_null(ColKey::Idx col_ndx) const; + + const ClusterTree* get_tree_top() const; + ColKey get_column_key(StringData col_name) const; + TableKey get_table_key() const; + TableRef get_target_table(ColKey col_key) const; + const Spec& get_spec() const; + + template + U _get(ColKey::Idx col_ndx) const; + + template + int cmp(const ConstObj& other, ColKey::Idx col_ndx) const; + int cmp(const ConstObj& other, ColKey::Idx col_ndx) const; + ObjKey get_backlink(ColKey backlink_col, size_t backlink_ndx) const; + std::vector get_all_backlinks(ColKey backlink_col) const; +}; + + +class Obj : public ConstObj { +public: + Obj() + { + } + Obj(TableRef table, MemRef mem, ObjKey key, size_t row_ndx); + + TableRef get_table() const + { + return m_table.cast_away_const(); + } + + + template + Obj& set(ColKey col_key, U value, bool is_default = false); + + Obj& set(ColKey col_key, Mixed value); + + template + Obj& set(StringData col_name, U value, bool is_default = false) + { + return set(get_column_key(col_name), value, is_default); + } + + Obj& set_null(ColKey col_key, bool is_default = false); + Obj& set_null(StringData col_name, bool is_default = false) + { + return set_null(get_column_key(col_name), is_default); + } + + Obj& add_int(ColKey col_key, int64_t value); + Obj& add_int(StringData col_name, int64_t value) + { + return add_int(get_column_key(col_name), value); + } + + template + Obj& set_list_values(ColKey col_key, const std::vector& values); + + template + std::vector get_list_values(ColKey col_key) const; + + template + Obj& set_all(Head v, Tail... tail); + + void assign(const ConstObj& other); + + Obj get_linked_object(ColKey link_col_key); + + template + Lst get_list(ColKey col_key) const; + template + LstPtr get_list_ptr(ColKey col_key) const; + + template + Lst get_list(StringData col_name) const + { + return get_list(get_column_key(col_name)); + } + + LnkLst get_linklist(ColKey col_key) const; + LnkLstPtr get_linklist_ptr(ColKey col_key) const; + LnkLst get_linklist(StringData col_name) const; + + LstBasePtr get_listbase_ptr(ColKey col_key) const; + +private: + friend class ArrayBacklink; + friend class CascadeState; + friend class Cluster; + friend class ConstLstBase; + friend class ConstObj; + template + friend class Lst; + friend class LnkLst; + friend class Table; + + Obj(const ConstObj& other) + : ConstObj(other) + { + } + template + Obj& _set(size_t col_ndx, Val v); + template + Obj& _set(size_t col_ndx, Head v, Tail... tail); + ColKey spec_ndx2colkey(size_t col_ndx); + bool ensure_writeable(); + void bump_content_version(); + void bump_both_versions(); + template + void do_set_null(ColKey col_key); + + void set_int(ColKey col_key, int64_t value); + void add_backlink(ColKey backlink_col, ObjKey origin_key); + bool remove_one_backlink(ColKey backlink_col, ObjKey origin_key); + void nullify_link(ColKey origin_col, ObjKey target_key); + // Used when inserting a new link. You will not remove existing links in this process + void set_backlink(ColKey col_key, ObjKey new_key); + // Used when replacing a link, return true if CascadeState contains objects to remove + bool replace_backlink(ColKey col_key, ObjKey old_key, ObjKey new_key, CascadeState& state); + // Used when removing a backlink, return true if CascadeState contains objects to remove + bool remove_backlink(ColKey col_key, ObjKey old_key, CascadeState& state); + template + inline void set_spec(T&, ColKey); +}; + + +inline Obj Obj::get_linked_object(ColKey link_col_key) +{ + return ConstObj::get_linked_object(link_col_key); +} + +template <> +Obj& Obj::set(ColKey, int64_t value, bool is_default); + +template <> +Obj& Obj::set(ColKey, ObjKey value, bool is_default); + + +template <> +inline Obj& Obj::set(ColKey col_key, int value, bool is_default) +{ + return set(col_key, int_fast64_t(value), is_default); +} + +template <> +inline Obj& Obj::set(ColKey col_key, uint_fast64_t value, bool is_default) +{ + int_fast64_t value_2 = 0; + if (REALM_UNLIKELY(int_cast_with_overflow_detect(value, value_2))) { + REALM_TERMINATE("Unsigned integer too big."); + } + return set(col_key, value_2, is_default); +} + +template <> +inline Obj& Obj::set(ColKey col_key, const char* str, bool is_default) +{ + return set(col_key, StringData(str), is_default); +} + +template <> +inline Obj& Obj::set(ColKey col_key, char* str, bool is_default) +{ + return set(col_key, StringData(str), is_default); +} + +template <> +inline Obj& Obj::set(ColKey col_key, std::string str, bool is_default) +{ + return set(col_key, StringData(str), is_default); +} + +template <> +inline Obj& Obj::set(ColKey col_key, realm::null, bool is_default) +{ + return set_null(col_key, is_default); +} + +template <> +inline Obj& Obj::set(ColKey col_key, Optional value, bool is_default) +{ + return value ? set(col_key, *value, is_default) : set_null(col_key, is_default); +} + +template <> +inline Obj& Obj::set(ColKey col_key, Optional value, bool is_default) +{ + return value ? set(col_key, *value, is_default) : set_null(col_key, is_default); +} + +template <> +inline Obj& Obj::set(ColKey col_key, Optional value, bool is_default) +{ + return value ? set(col_key, *value, is_default) : set_null(col_key, is_default); +} + +template <> +inline Obj& Obj::set(ColKey col_key, Optional value, bool is_default) +{ + return value ? set(col_key, *value, is_default) : set_null(col_key, is_default); +} + +template +Obj& Obj::set_list_values(ColKey col_key, const std::vector& values) +{ + size_t sz = values.size(); + auto list = get_list(col_key); + list.resize(sz); + for (size_t i = 0; i < sz; i++) + list.set(i, values[i]); + + return *this; +} + +template +std::vector Obj::get_list_values(ColKey col_key) const +{ + std::vector values; + auto list = get_list(col_key); + for (auto v : list) + values.push_back(v); + + return values; +} + +template +inline Obj& Obj::_set(size_t col_ndx, Val v) +{ + return set(spec_ndx2colkey(col_ndx), v); +} + +template +inline Obj& Obj::_set(size_t col_ndx, Head v, Tail... tail) +{ + set(spec_ndx2colkey(col_ndx), v); + return _set(col_ndx + 1, tail...); +} + +template +inline Obj& Obj::set_all(Head v, Tail... tail) +{ + return _set(0, v, tail...); +} +} + +#endif // REALM_OBJ_HPP diff --git a/src/vendor-include/realm-ios/include/realm/obj_list.hpp b/src/vendor-include/realm-ios/include/realm/obj_list.hpp new file mode 100644 index 000000000..e6a040008 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/obj_list.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_OBJ_LIST_HPP +#define REALM_OBJ_LIST_HPP + +#include +#include +#include +#include + +namespace realm { + +class DescriptorOrdering; +class Table; +class ConstTableView; + +class ObjList { +public: + ObjList(KeyColumn* key_values); + ObjList(KeyColumn* key_values, ConstTableRef parent); + + virtual ~ObjList() + { +#ifdef REALM_COOKIE_CHECK + m_debug_cookie = 0x7765697633333333; // 0x77656976 = 'view'; 0x33333333 = '3333' = destructed +#endif + } + + const Table& get_parent() const noexcept + { + return *m_table; + } + + virtual size_t size() const; + + // Get the number of total results which have been filtered out because a number of "LIMIT" operations have + // been applied. This number only applies to the last sync. + size_t get_num_results_excluded_by_limit() const noexcept + { + return m_limit_count; + } + + // Get key for object this view is "looking" at. + ObjKey get_key(size_t ndx) const; + + ConstObj try_get_object(size_t row_ndx) const; + ConstObj get_object(size_t row_ndx) const; + ConstObj front() const noexcept + { + return get_object(0); + } + ConstObj back() const noexcept + { + size_t last_row_ndx = size() - 1; + return get_object(last_row_ndx); + } + ConstObj operator[](size_t row_ndx) const noexcept + { + return get_object(row_ndx); + } + + template + void for_each(F func) const; + + template + ConstTableView find_all(ColKey column_key, T value); + + template + size_t find_first(ColKey column_key, T value); + + // Get the versions of all tables which this list depends on + TableVersions get_dependency_versions() const; + + // These three methods are overridden by TableView and ObjList/LnkLst. + virtual void sync_if_needed() const = 0; + virtual void get_dependencies(TableVersions&) const = 0; + virtual bool is_in_sync() const = 0; + void check_cookie() const + { +#ifdef REALM_COOKIE_CHECK + REALM_ASSERT_RELEASE(m_debug_cookie == cookie_expected); +#endif + } + +protected: + friend class Query; + static const uint64_t cookie_expected = 0x7765697677777777ull; // 0x77656976 = 'view'; 0x77777777 = '7777' = alive + + // Null if, and only if, the view is detached. + mutable ConstTableRef m_table; + KeyColumn* m_key_values = nullptr; + size_t m_limit_count = 0; + uint64_t m_debug_cookie; + + void assign(KeyColumn* key_values, ConstTableRef parent); + + void do_sort(const DescriptorOrdering&); + void detach() const noexcept // may have to remove const + { + m_table = TableRef(); + } +}; + +template +inline void ObjList::for_each(F func) const +{ + auto sz = size(); + for (size_t i = 0; i < sz; i++) { + auto o = try_get_object(i); + if (o && func(o)) + return; + } +} + +template +size_t ObjList::find_first(ColKey column_key, T value) +{ + auto sz = size(); + for (size_t i = 0; i < sz; i++) { + auto o = try_get_object(i); + if (o) { + T v = o.get(column_key); + if (v == value) + return i; + } + } + return realm::npos; +} +} + +#endif /* SRC_REALM_OBJ_LIST_HPP_ */ diff --git a/src/vendor-include/realm-ios/include/realm/owned_data.hpp b/src/vendor-include/realm-ios/include/realm/owned_data.hpp new file mode 100644 index 000000000..c707f9da4 --- /dev/null +++ b/src/vendor-include/realm-ios/include/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/src/vendor-include/realm-ios/include/realm/parser/collection_operator_expression.hpp b/src/vendor-include/realm-ios/include/realm/parser/collection_operator_expression.hpp new file mode 100644 index 000000000..f357377c0 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/parser/collection_operator_expression.hpp @@ -0,0 +1,269 @@ +//////////////////////////////////////////////////////////////////////////// +// +// 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 REALM_COLLECTION_OPERATOR_EXPRESSION_HPP +#define REALM_COLLECTION_OPERATOR_EXPRESSION_HPP + +#include "property_expression.hpp" +#include "parser.hpp" +#include "parser_utils.hpp" + +#include + +namespace realm { +namespace parser { + +template +struct CollectionOperatorGetter; + +template +struct CollectionOperatorExpression +{ + static constexpr parser::Expression::KeyPathOp operation_type = OpType; + std::function link_chain_getter; + PropertyExpression pe; + ColKey post_link_col_key; + DataType post_link_col_type; + + CollectionOperatorExpression(PropertyExpression&& exp, std::string suffix_path, parser::KeyPathMapping& mapping) + : pe(std::move(exp)) + { + link_chain_getter = std::bind(&PropertyExpression::link_chain_getter, pe); + + const bool requires_suffix_path = + !(OpType == parser::Expression::KeyPathOp::SizeString || + OpType == parser::Expression::KeyPathOp::SizeBinary || OpType == parser::Expression::KeyPathOp::Count || + OpType == parser::Expression::KeyPathOp::BacklinkCount); + + if (requires_suffix_path) { + const Table* pre_link_table = pe.link_chain_getter().get_base_table(); + REALM_ASSERT(pre_link_table); + StringData list_property_name; + if (pe.dest_type_is_backlink()) { + list_property_name = "linking object"; + } + else { + list_property_name = pre_link_table->get_column_name(pe.get_dest_col_key()); + } + realm_precondition( + pe.get_dest_type() == type_LinkList || pe.dest_type_is_backlink(), + util::format("The '%1' operation must be used on a list property, but '%2' is not a list", + util::collection_operator_to_str(OpType), list_property_name)); + + ConstTableRef post_link_table; + if (pe.dest_type_is_backlink()) { + post_link_table = pe.get_dest_table(); + } + else { + post_link_table = pe.get_dest_table()->get_link_target(pe.get_dest_col_key()); + } + REALM_ASSERT(post_link_table); + StringData printable_post_link_table_name = get_printable_table_name(*post_link_table); + + KeyPath suffix_key_path = key_path_from_string(suffix_path); + + realm_precondition(suffix_path.size() > 0 && suffix_key_path.size() > 0, + util::format("A property from object '%1' must be provided to perform operation '%2'", + printable_post_link_table_name, + util::collection_operator_to_str(OpType))); + size_t index = 0; + KeyPathElement element = mapping.process_next_path(post_link_table, suffix_key_path, index); + + realm_precondition( + suffix_key_path.size() == 1, + util::format("Unable to use '%1' because collection aggreate operations are only supported " + "for direct properties at this time", + suffix_path)); + + post_link_col_key = element.col_key; + post_link_col_type = element.col_type; + } + else { // !requires_suffix_path + if (!pe.link_chain.empty()) { + post_link_col_type = pe.get_dest_type(); + } + + realm_precondition(suffix_path.empty(), + util::format("An extraneous property '%1' was found for operation '%2'", suffix_path, + util::collection_operator_to_str(OpType))); + } + } + template + auto value_of_type_for_query() const + { + return CollectionOperatorGetter::convert(*this); + } +}; + + +// Certain operations are disabled for some types (eg. a sum of timestamps is invalid). +// The operations that are supported have a specialisation with std::enable_if for that type below +// any type/operation combination that is not specialised will get the runtime error from the following +// default implementation. The return type is just a dummy to make things compile. +template +struct CollectionOperatorGetter { + static Columns convert(const CollectionOperatorExpression& op) { + throw std::runtime_error(util::format("Predicate error: comparison of type '%1' with result of '%2' is not supported.", + type_to_str(), + collection_operator_to_str(op.operation_type))); + } +}; + +template +struct CollectionOperatorGetter::value>> { + static SubColumnAggregate > convert(const CollectionOperatorExpression& expr) + { + if (expr.pe.dest_type_is_backlink()) { + return expr.link_chain_getter() + .template column(*expr.pe.get_dest_table(), expr.pe.get_dest_col_key()) + .template column(expr.post_link_col_key) + .min(); + } + else { + return expr.link_chain_getter() + .template column(expr.pe.get_dest_col_key()) + .template column(expr.post_link_col_key) + .min(); + } + } +}; + +template +struct CollectionOperatorGetter::value>> { + static SubColumnAggregate > convert(const CollectionOperatorExpression& expr) + { + if (expr.pe.dest_type_is_backlink()) { + return expr.link_chain_getter() + .template column(*expr.pe.get_dest_table(), expr.pe.get_dest_col_key()) + .template column(expr.post_link_col_key) + .max(); + } + else { + return expr.link_chain_getter() + .template column(expr.pe.get_dest_col_key()) + .template column(expr.post_link_col_key) + .max(); + } + } +}; + +template +struct CollectionOperatorGetter::value>> { + static SubColumnAggregate > convert(const CollectionOperatorExpression& expr) + { + if (expr.pe.dest_type_is_backlink()) { + return expr.link_chain_getter() + .template column(*expr.pe.get_dest_table(), expr.pe.get_dest_col_key()) + .template column(expr.post_link_col_key) + .sum(); + } + else { + return expr.link_chain_getter() + .template column(expr.pe.get_dest_col_key()) + .template column(expr.post_link_col_key) + .sum(); + } + } +}; + +template +struct CollectionOperatorGetter::value>> { + static SubColumnAggregate > convert(const CollectionOperatorExpression& expr) + { + if (expr.pe.dest_type_is_backlink()) { + return expr.link_chain_getter() + .template column(*expr.pe.get_dest_table(), expr.pe.get_dest_col_key()) + .template column(expr.post_link_col_key) + .average(); + } + else { + return expr.link_chain_getter() + .template column(expr.pe.get_dest_col_key()) + .template column(expr.post_link_col_key) + .average(); + } + } +}; + +template +struct CollectionOperatorGetter::value>> { + static LinkCount convert(const CollectionOperatorExpression& expr) + { + if (expr.pe.dest_type_is_backlink()) { + return expr.link_chain_getter() + .template column(*expr.pe.get_dest_table(), expr.pe.get_dest_col_key()) + .count(); + } + else { + return expr.link_chain_getter().template column(expr.pe.get_dest_col_key()).count(); + } + } +}; + + +template +struct CollectionOperatorGetter::value>> { + static BacklinkCount + convert(const CollectionOperatorExpression& expr) + { + if (expr.pe.link_chain.empty() || expr.pe.get_dest_col_key() == ColKey()) { + // here we are operating on the current table from a "@links.@count" query with no link keypath prefix + return expr.link_chain_getter().template get_backlink_count(); + } + else { + if (expr.pe.dest_type_is_backlink()) { + return expr.link_chain_getter() + .template column(*expr.pe.get_dest_table(), expr.pe.get_dest_col_key()) + .template backlink_count(); + } + else { + return expr.link_chain_getter() + .template column(expr.pe.get_dest_col_key()) + .template backlink_count(); + } + } + } +}; + + +template <> +struct CollectionOperatorGetter{ + static SizeOperator convert(const CollectionOperatorExpression& expr) + { + return expr.link_chain_getter().template column(expr.pe.get_dest_col_key()).size(); + } +}; + +template <> +struct CollectionOperatorGetter{ + static SizeOperator convert(const CollectionOperatorExpression& expr) + { + return expr.link_chain_getter().template column(expr.pe.get_dest_col_key()).size(); + } +}; + +} // namespace parser +} // namespace realm + +#endif // REALM_COLLECTION_OPERATOR_EXPRESSION_HPP diff --git a/src/vendor-include/realm-ios/include/realm/parser/expression_container.hpp b/src/vendor-include/realm-ios/include/realm/parser/expression_container.hpp new file mode 100644 index 000000000..0629bfaa1 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/parser/expression_container.hpp @@ -0,0 +1,79 @@ +//////////////////////////////////////////////////////////////////////////// +// +// 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 REALM_EXPRESSION_CONTAINER_HPP +#define REALM_EXPRESSION_CONTAINER_HPP + +#include + +#include "collection_operator_expression.hpp" +#include "parser.hpp" +#include "property_expression.hpp" +#include "query_builder.hpp" +#include "subquery_expression.hpp" +#include "value_expression.hpp" + +namespace realm { +namespace parser { + +class ExpressionContainer +{ +public: + ExpressionContainer(Query& query, const parser::Expression& e, query_builder::Arguments& args, + parser::KeyPathMapping& mapping); + + bool is_null(); + + PropertyExpression& get_property(); + ValueExpression& get_value(); + CollectionOperatorExpression& get_min(); + CollectionOperatorExpression& get_max(); + CollectionOperatorExpression& get_sum(); + CollectionOperatorExpression& get_avg(); + CollectionOperatorExpression& get_count(); + CollectionOperatorExpression& get_backlink_count(); + CollectionOperatorExpression& get_size_string(); + CollectionOperatorExpression& get_size_binary(); + SubqueryExpression& get_subexpression(); + + DataType check_type_compatibility(DataType type); + DataType get_comparison_type(ExpressionContainer& rhs); + + enum class ExpressionInternal { + exp_Value, + exp_Property, + exp_OpMin, + exp_OpMax, + exp_OpSum, + exp_OpAvg, + exp_OpCount, + exp_OpSizeString, + exp_OpSizeBinary, + exp_OpBacklinkCount, + exp_SubQuery + }; + + ExpressionInternal type; +private: + util::Any storage; +}; + +} // namespace parser +} // namespace realm + +#endif // REALM_EXPRESSION_CONTAINER_HPP diff --git a/src/vendor-include/realm-ios/include/realm/parser/keypath_mapping.hpp b/src/vendor-include/realm-ios/include/realm/parser/keypath_mapping.hpp new file mode 100644 index 000000000..96019eb6f --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/parser/keypath_mapping.hpp @@ -0,0 +1,81 @@ +//////////////////////////////////////////////////////////////////////////// +// +// 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 REALM_KEYPATH_MAPPING_HPP +#define REALM_KEYPATH_MAPPING_HPP + +#include + +#include "parser_utils.hpp" + +#include +#include + +namespace realm { +namespace parser { + +struct KeyPathElement { + ConstTableRef table; + ColKey col_key; + DataType col_type; + bool is_backlink; +}; + +class BacklinksRestrictedError : public std::runtime_error { +public: + BacklinksRestrictedError(const std::string& msg) + : std::runtime_error(msg) + { + } + /// runtime_error::what() returns the msg provided in the constructor. +}; + +struct TableAndColHash { + std::size_t operator()(const std::pair& p) const; +}; + + +// This class holds state which allows aliasing variable names in key paths used in queries. +// It is used to allow variable naming in subqueries such as 'SUBQUERY(list, $obj, $obj.intCol = 5).@count' +// It can also be used to allow querying named backlinks if bindings provide the mappings themselves. +class KeyPathMapping { +public: + KeyPathMapping(); + // returns true if added, false if duplicate key already exists + bool add_mapping(ConstTableRef table, std::string name, std::string alias); + void remove_mapping(ConstTableRef table, std::string name); + bool has_mapping(ConstTableRef table, std::string name); + KeyPathElement process_next_path(ConstTableRef table, KeyPath& path, size_t& index); + void set_allow_backlinks(bool allow); + bool backlinks_allowed() const + { + return m_allow_backlinks; + } + void set_backlink_class_prefix(std::string prefix); + static LinkChain link_chain_getter(ConstTableRef table, const std::vector& links); + +protected: + bool m_allow_backlinks; + std::string m_backlink_class_prefix; + std::unordered_map, std::string, TableAndColHash> m_mapping; +}; + +} // namespace parser +} // namespace realm + +#endif // REALM_KEYPATH_MAPPING_HPP diff --git a/src/vendor-include/realm-ios/include/realm/parser/parser.hpp b/src/vendor-include/realm-ios/include/realm/parser/parser.hpp new file mode 100644 index 000000000..3c8410618 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/parser/parser.hpp @@ -0,0 +1,142 @@ +//////////////////////////////////////////////////////////////////////////// +// +// 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 REALM_PARSER_HPP +#define REALM_PARSER_HPP + +#include +#include +#include +#include + +namespace realm { + +namespace parser { + +struct Predicate; + +struct Expression +{ + enum class Type { None, Number, String, KeyPath, Argument, True, False, Null, Timestamp, Base64, SubQuery } type; + enum class KeyPathOp { None, Min, Max, Avg, Sum, Count, SizeString, SizeBinary, BacklinkCount } collection_op; + std::string s; + std::vector time_inputs; + std::string op_suffix; + std::string subquery_path, subquery_var; + std::shared_ptr subquery; + Expression(Type t = Type::None, std::string input = "") : type(t), collection_op(KeyPathOp::None), s(input) {} + Expression(std::vector&& timestamp) : type(Type::Timestamp), collection_op(KeyPathOp::None), time_inputs(timestamp) {} + Expression(std::string prefix, KeyPathOp op, std::string suffix) : type(Type::KeyPath), collection_op(op), s(prefix), op_suffix(suffix) {} +}; + +struct Predicate +{ + enum class Type + { + Comparison, + Or, + And, + True, + False + } type = Type::And; + + enum class Operator { + None, + Equal, + NotEqual, + LessThan, + LessThanOrEqual, + GreaterThan, + GreaterThanOrEqual, + BeginsWith, + EndsWith, + Contains, + Like, + In + }; + + enum class OperatorOption + { + None, + CaseInsensitive, + }; + + enum class ComparisonType { + Unspecified, + Any, + All, + None, + }; + + struct Comparison + { + Operator op = Operator::None; + OperatorOption option = OperatorOption::None; + Expression expr[2] = {{Expression::Type::None, ""}, {Expression::Type::None, ""}}; + ComparisonType compare_type = ComparisonType::Unspecified; + }; + + struct Compound + { + std::vector sub_predicates; + }; + + Comparison cmpr; + Compound cpnd; + + bool negate = false; + + Predicate(Type t, bool n = false) : type(t), negate(n) {} +}; + +struct DescriptorOrderingState +{ + struct PropertyState + { + std::string key_path; + std::string table_name; + bool ascending; + }; + struct SingleOrderingState + { + std::vector properties; + size_t limit; + enum class DescriptorType { Sort, Distinct, Limit, Include } type; + }; + std::vector orderings; +}; + +struct ParserResult +{ + Predicate predicate; + DescriptorOrderingState ordering; +}; + +ParserResult parse(const char* query); // assumes c-style null termination +ParserResult parse(const std::string& query); +ParserResult parse(const realm::StringData& query); + +DescriptorOrderingState parse_include_path(const realm::StringData& path); + +// run the analysis tool to check for cycles in the grammar +// returns the number of problems found and prints some info to std::cout +size_t analyze_grammar(); +} +} + +#endif // REALM_PARSER_HPP diff --git a/src/vendor-include/realm-ios/include/realm/parser/parser_utils.hpp b/src/vendor-include/realm-ios/include/realm/parser/parser_utils.hpp new file mode 100644 index 000000000..3f335d381 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/parser/parser_utils.hpp @@ -0,0 +1,90 @@ +//////////////////////////////////////////////////////////////////////////// +// +// 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 REALM_PARSER_UTILS_HPP +#define REALM_PARSER_UTILS_HPP + +#include +#include +#include "parser.hpp" + +#include +#include + +namespace realm { + +class Table; +class Timestamp; +struct Link; + +namespace util { + +// 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 +#define realm_precondition(condition, message) \ + if (!REALM_LIKELY(condition)) { \ + throw std::logic_error(message); \ + } + + +template +const char* type_to_str(); + +template <> +const char* type_to_str(); +template <> +const char* type_to_str(); +template <> +const char* type_to_str(); +template <> +const char* type_to_str(); +template <> +const char* type_to_str(); +template <> +const char* type_to_str(); +template <> +const char* type_to_str(); +template <> +const char* type_to_str(); + +const char* data_type_to_str(DataType type); +const char* collection_operator_to_str(parser::Expression::KeyPathOp op); +const char* comparison_type_to_str(parser::Predicate::ComparisonType type); + +using KeyPath = std::vector; +KeyPath key_path_from_string(const std::string &s); +std::string key_path_to_string(const KeyPath& keypath); +StringData get_printable_table_name(StringData name); +StringData get_printable_table_name(const Table& table); + +template +T stot(std::string const& s) { + std::istringstream iss(s); + T value; + iss >> value; + if (iss.fail()) { + throw std::invalid_argument(util::format("Cannot convert string '%1'", s)); + } + return value; +} + +} // namespace utils +} // namespace realm + +#endif // REALM_PARSER_UTILS_HPP diff --git a/src/vendor-include/realm-ios/include/realm/parser/property_expression.hpp b/src/vendor-include/realm-ios/include/realm/parser/property_expression.hpp new file mode 100644 index 000000000..57c5bc8b0 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/parser/property_expression.hpp @@ -0,0 +1,78 @@ +//////////////////////////////////////////////////////////////////////////// +// +// 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 REALM_PROPERTY_EXPRESSION_HPP +#define REALM_PROPERTY_EXPRESSION_HPP + +#include +#include +#include + +namespace realm { +namespace parser { + +struct PropertyExpression +{ + Query &query; + std::vector link_chain; + DataType get_dest_type() const; + ColKey get_dest_col_key() const; + ConstTableRef get_dest_table() const; + bool dest_type_is_backlink() const; + + PropertyExpression(Query& q, const std::string& key_path_string, parser::KeyPathMapping& mapping); + + LinkChain link_chain_getter() const; + + template + auto value_of_type_for_query() const + { + return this->link_chain_getter().template column(get_dest_col_key()); + } +}; + +inline DataType PropertyExpression::get_dest_type() const +{ + REALM_ASSERT_DEBUG(link_chain.size() > 0); + return link_chain.back().col_type; +} + +inline bool PropertyExpression::dest_type_is_backlink() const +{ + REALM_ASSERT_DEBUG(link_chain.size() > 0); + return link_chain.back().is_backlink; +} + +inline ColKey PropertyExpression::get_dest_col_key() const +{ + REALM_ASSERT_DEBUG(link_chain.size() > 0); + return link_chain.back().col_key; +} + +inline ConstTableRef PropertyExpression::get_dest_table() const +{ + REALM_ASSERT_DEBUG(link_chain.size() > 0); + return link_chain.back().table; +} + + +} // namespace parser +} // namespace realm + +#endif // REALM_PROPERTY_EXPRESSION_HPP + diff --git a/src/vendor-include/realm-ios/include/realm/parser/query_builder.hpp b/src/vendor-include/realm-ios/include/realm/parser/query_builder.hpp new file mode 100644 index 000000000..23cf1cff8 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/parser/query_builder.hpp @@ -0,0 +1,193 @@ +//////////////////////////////////////////////////////////////////////////// +// +// 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 REALM_QUERY_BUILDER_HPP +#define REALM_QUERY_BUILDER_HPP + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace realm { +class Query; +class Realm; +class Table; +template class BasicRowExpr; +using RowExpr = BasicRowExpr; + +namespace parser { + struct Predicate; + struct DescriptorOrderingState; +} + +namespace query_builder { +class Arguments; + +void apply_predicate(Query& query, const parser::Predicate& predicate, Arguments& arguments, + parser::KeyPathMapping mapping = parser::KeyPathMapping()); + +void apply_ordering(DescriptorOrdering& ordering, ConstTableRef target, const parser::DescriptorOrderingState& state, + Arguments& arguments, parser::KeyPathMapping mapping = parser::KeyPathMapping()); +void apply_ordering(DescriptorOrdering& ordering, ConstTableRef target, const parser::DescriptorOrderingState& state, + parser::KeyPathMapping mapping = parser::KeyPathMapping()); + + +struct AnyContext +{ + template + T unbox(const util::Any& wrapper) { + return util::any_cast(wrapper); + } + bool is_null(const util::Any& wrapper) { + if (!wrapper.has_value()) { + return true; + } + if (wrapper.type() == typeid(realm::null)) { + return true; + } + return false; + } +}; + +class Arguments { +public: + virtual bool bool_for_argument(size_t argument_index) = 0; + virtual long long long_for_argument(size_t argument_index) = 0; + virtual float float_for_argument(size_t argument_index) = 0; + virtual double double_for_argument(size_t argument_index) = 0; + virtual StringData string_for_argument(size_t argument_index) = 0; + virtual BinaryData binary_for_argument(size_t argument_index) = 0; + virtual Timestamp timestamp_for_argument(size_t argument_index) = 0; + virtual ObjKey object_index_for_argument(size_t argument_index) = 0; + virtual bool is_argument_null(size_t argument_index) = 0; + // dynamic conversion space with lifetime tied to this + // it is used for storing literal binary/string data + std::vector buffer_space; +}; + +template +class ArgumentConverter : public Arguments { +public: + ArgumentConverter(ContextType& context, const ValueType* arguments, size_t count) + : m_ctx(context) + , m_arguments(arguments) + , m_count(count) + {} + + bool bool_for_argument(size_t i) override { return get(i); } + long long long_for_argument(size_t i) override { return get(i); } + float float_for_argument(size_t i) override { return get(i); } + double double_for_argument(size_t i) override { return get(i); } + StringData string_for_argument(size_t i) override { return get(i); } + BinaryData binary_for_argument(size_t i) override { return get(i); } + Timestamp timestamp_for_argument(size_t i) override { return get(i); } + ObjKey object_index_for_argument(size_t i) override + { + return get(i); + } + bool is_argument_null(size_t i) override { return m_ctx.is_null(at(i)); } + +private: + ContextType& m_ctx; + const ValueType* m_arguments; + size_t m_count; + + const ValueType& at(size_t index) const + { + if (index >= m_count) { + std::string error_message; + if (m_count) { + error_message = util::format("Request for argument at index %1 but only %2 argument%3 provided", + index, m_count, m_count == 1 ? " is" : "s are"); + } + else { + error_message = util::format("Request for argument at index %1 but no arguments are provided", index); + } + throw std::out_of_range(error_message); + } + return m_arguments[index]; + } + + template + T get(size_t index) const + { + return m_ctx.template unbox(at(index)); + } +}; + +class NoArgsError : public std::runtime_error { +public: + NoArgsError() + : std::runtime_error("Attempt to retreive an argument when no arguments were given") + { + } +}; + +class NoArguments : public Arguments { +public: + bool bool_for_argument(size_t) + { + throw NoArgsError(); + } + long long long_for_argument(size_t) + { + throw NoArgsError(); + } + float float_for_argument(size_t) + { + throw NoArgsError(); + } + double double_for_argument(size_t) + { + throw NoArgsError(); + } + StringData string_for_argument(size_t) + { + throw NoArgsError(); + } + BinaryData binary_for_argument(size_t) + { + throw NoArgsError(); + } + Timestamp timestamp_for_argument(size_t) + { + throw NoArgsError(); + } + ObjKey object_index_for_argument(size_t) + { + throw NoArgsError(); + } + bool is_argument_null(size_t) + { + throw NoArgsError(); + } +}; + +} // namespace query_builder +} // namespace realm + +#endif // REALM_QUERY_BUILDER_HPP diff --git a/src/vendor-include/realm-ios/include/realm/parser/subquery_expression.hpp b/src/vendor-include/realm-ios/include/realm/parser/subquery_expression.hpp new file mode 100644 index 000000000..e22d62770 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/parser/subquery_expression.hpp @@ -0,0 +1,115 @@ +//////////////////////////////////////////////////////////////////////////// +// +// 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 REALM_SUBQUERY_EXPRESSION_HPP +#define REALM_SUBQUERY_EXPRESSION_HPP + +#include +#include +#include +#include + +#include "parser_utils.hpp" + +namespace realm { +namespace parser { + +template +struct SubqueryGetter; + +struct SubqueryExpression { + std::string var_name; + Query& query; + Query subquery; + std::vector link_chain; + DataType get_dest_type() const; + ColKey get_dest_col_key() const; + ConstTableRef get_dest_table() const; + bool dest_type_is_backlink() const; + + + SubqueryExpression(Query& q, const std::string& key_path_string, const std::string& variable_name, + parser::KeyPathMapping& mapping); + Query& get_subquery(); + + LinkChain link_chain_getter() const; + + template + auto value_of_type_for_query() const + { + return SubqueryGetter::convert(*this); + } +}; + +inline DataType SubqueryExpression::get_dest_type() const +{ + REALM_ASSERT_DEBUG(link_chain.size() > 0); + return link_chain.back().col_type; +} + +inline bool SubqueryExpression::dest_type_is_backlink() const +{ + REALM_ASSERT_DEBUG(link_chain.size() > 0); + return link_chain.back().is_backlink; +} + +inline ColKey SubqueryExpression::get_dest_col_key() const +{ + REALM_ASSERT_DEBUG(link_chain.size() > 0); + return link_chain.back().col_key; +} + +inline ConstTableRef SubqueryExpression::get_dest_table() const +{ + REALM_ASSERT_DEBUG(link_chain.size() > 0); + return link_chain.back().table; +} + +// Certain operations are disabled for some types (eg. a sum of timestamps is invalid). +// The operations that are supported have a specialisation with std::enable_if for that type below +// any type/operation combination that is not specialised will get the runtime error from the following +// default implementation. The return type is just a dummy to make things compile. +template +struct SubqueryGetter { + static Columns convert(const SubqueryExpression&) + { + throw std::runtime_error( + util::format("Predicate error: comparison of type '%1' with result of a subquery count is not supported.", + type_to_str())); + } +}; + +template +struct SubqueryGetter::value>> { + static SubQueryCount convert(const SubqueryExpression& expr) + { + if (expr.dest_type_is_backlink()) { + return expr.link_chain_getter() + .template column(*expr.get_dest_table(), expr.get_dest_col_key(), expr.subquery) + .count(); + } + else { + return expr.link_chain_getter().template column(expr.get_dest_col_key(), expr.subquery).count(); + } + } +}; + +} // namespace parser +} // namespace realm + +#endif // REALM_SUBQUERY_EXPRESSION_HPP diff --git a/src/vendor-include/realm-ios/include/realm/parser/value_expression.hpp b/src/vendor-include/realm-ios/include/realm/parser/value_expression.hpp new file mode 100644 index 000000000..daf436756 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/parser/value_expression.hpp @@ -0,0 +1,42 @@ +//////////////////////////////////////////////////////////////////////////// +// +// 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 REALM_VALUE_EXPRESSION_HPP +#define REALM_VALUE_EXPRESSION_HPP + +#include "parser.hpp" +#include "query_builder.hpp" + +namespace realm { +namespace parser { + +struct ValueExpression +{ + const parser::Expression* value; + query_builder::Arguments* arguments; + + ValueExpression(query_builder::Arguments* args, const parser::Expression* v); + bool is_null(); + template + RetType value_of_type_for_query(); +}; + +} // namespace parser +} // namespace realm + +#endif // REALM_VALUE_EXPRESSION_HPP diff --git a/src/vendor-include/realm-ios/include/realm/query.hpp b/src/vendor-include/realm-ios/include/realm/query.hpp new file mode 100644 index 000000000..a2c95d5e4 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/query.hpp @@ -0,0 +1,395 @@ +/************************************************************************* + * + * 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 + +namespace realm { + + +// Pre-declarations +class ParentNode; +class Table; +class TableView; +class ConstTableView; +class Array; +class Expression; +class Group; +class Transaction; + +namespace metrics { +class QueryInfo; +} + +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&, Transaction*); + + 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(ConstTableRef table, ConstTableView* tv = nullptr); + Query(ConstTableRef table, std::unique_ptr); + Query(ConstTableRef table, const LnkLst& list); + Query(ConstTableRef table, LnkLstPtr&& list); + 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(ColKey column_key, ObjKey target_key); + // Find links that point to specific target objects + Query& links_to(ColKey column_key, const std::vector& target_obj); + + // Conditions: null + Query& equal(ColKey column_key, null); + Query& not_equal(ColKey column_key, null); + + // Conditions: int64_t + Query& equal(ColKey column_key, int64_t value); + Query& not_equal(ColKey column_key, int64_t value); + Query& greater(ColKey column_key, int64_t value); + Query& greater_equal(ColKey column_key, int64_t value); + Query& less(ColKey column_key, int64_t value); + Query& less_equal(ColKey column_key, int64_t value); + Query& between(ColKey column_key, int64_t from, int64_t to); + + // Conditions: int (we need those because conversion from '1234' is ambiguous with float/double) + Query& equal(ColKey column_key, int value); + Query& not_equal(ColKey column_key, int value); + Query& greater(ColKey column_key, int value); + Query& greater_equal(ColKey column_key, int value); + Query& less(ColKey column_key, int value); + Query& less_equal(ColKey column_key, int value); + Query& between(ColKey column_key, int from, int to); + + // Conditions: 2 int columns + Query& equal_int(ColKey column_key1, ColKey column_key2); + Query& not_equal_int(ColKey column_key1, ColKey column_key2); + Query& greater_int(ColKey column_key1, ColKey column_key2); + Query& less_int(ColKey column_key1, ColKey column_key2); + Query& greater_equal_int(ColKey column_key1, ColKey column_key2); + Query& less_equal_int(ColKey column_key1, ColKey column_key2); + + // Conditions: float + Query& equal(ColKey column_key, float value); + Query& not_equal(ColKey column_key, float value); + Query& greater(ColKey column_key, float value); + Query& greater_equal(ColKey column_key, float value); + Query& less(ColKey column_key, float value); + Query& less_equal(ColKey column_key, float value); + Query& between(ColKey column_key, float from, float to); + + // Conditions: 2 float columns + Query& equal_float(ColKey column_key1, ColKey column_key2); + Query& not_equal_float(ColKey column_key1, ColKey column_key2); + Query& greater_float(ColKey column_key1, ColKey column_key2); + Query& greater_equal_float(ColKey column_key1, ColKey column_key2); + Query& less_float(ColKey column_key1, ColKey column_key2); + Query& less_equal_float(ColKey column_key1, ColKey column_key2); + + // Conditions: double + Query& equal(ColKey column_key, double value); + Query& not_equal(ColKey column_key, double value); + Query& greater(ColKey column_key, double value); + Query& greater_equal(ColKey column_key, double value); + Query& less(ColKey column_key, double value); + Query& less_equal(ColKey column_key, double value); + Query& between(ColKey column_key, double from, double to); + + // Conditions: 2 double columns + Query& equal_double(ColKey column_key1, ColKey column_key2); + Query& not_equal_double(ColKey column_key1, ColKey column_key2); + Query& greater_double(ColKey column_key1, ColKey column_key2); + Query& greater_equal_double(ColKey column_key1, ColKey column_key2); + Query& less_double(ColKey column_key1, ColKey column_key2); + Query& less_equal_double(ColKey column_key1, ColKey column_key2); + + // Conditions: timestamp + Query& equal(ColKey column_key, Timestamp value); + Query& not_equal(ColKey column_key, Timestamp value); + Query& greater(ColKey column_key, Timestamp value); + Query& greater_equal(ColKey column_key, Timestamp value); + Query& less_equal(ColKey column_key, Timestamp value); + Query& less(ColKey column_key, Timestamp value); + + // Conditions: size + Query& size_equal(ColKey column_key, int64_t value); + Query& size_not_equal(ColKey column_key, int64_t value); + Query& size_greater(ColKey column_key, int64_t value); + Query& size_greater_equal(ColKey column_key, int64_t value); + Query& size_less_equal(ColKey column_key, int64_t value); + Query& size_less(ColKey column_key, int64_t value); + Query& size_between(ColKey column_key, int64_t from, int64_t to); + + // Conditions: bool + Query& equal(ColKey column_key, bool value); + Query& not_equal(ColKey column_key, bool value); + + // Conditions: strings + Query& equal(ColKey column_key, StringData value, bool case_sensitive = true); + Query& not_equal(ColKey column_key, StringData value, bool case_sensitive = true); + Query& begins_with(ColKey column_key, StringData value, bool case_sensitive = true); + Query& ends_with(ColKey column_key, StringData value, bool case_sensitive = true); + Query& contains(ColKey column_key, StringData value, bool case_sensitive = true); + Query& like(ColKey column_key, 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(ColKey column_key, const char* c_str, bool case_sensitive = true); + Query& not_equal(ColKey column_key, const char* c_str, bool case_sensitive = true); + + // Conditions: binary data + Query& equal(ColKey column_key, BinaryData value, bool case_sensitive = true); + Query& not_equal(ColKey column_key, BinaryData value, bool case_sensitive = true); + Query& begins_with(ColKey column_key, BinaryData value, bool case_sensitive = true); + Query& ends_with(ColKey column_key, BinaryData value, bool case_sensitive = true); + Query& contains(ColKey column_key, BinaryData value, bool case_sensitive = true); + Query& like(ColKey column_key, BinaryData b, bool case_sensitive = true); + + // Negation + Query& Not(); + + // Grouping + Query& group(); + Query& end_group(); + 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 + ObjKey find(); + TableView find_all(size_t start = 0, size_t end = size_t(-1), size_t limit = size_t(-1)); + + // Aggregates + size_t count() const; + TableView find_all(const DescriptorOrdering& descriptor); + size_t count(const DescriptorOrdering& descriptor); + int64_t sum_int(ColKey column_key) const; + double average_int(ColKey column_key, size_t* resultcount = nullptr) const; + int64_t maximum_int(ColKey column_key, ObjKey* return_ndx = nullptr) const; + int64_t minimum_int(ColKey column_key, ObjKey* return_ndx = nullptr) const; + double sum_float(ColKey column_key) const; + double average_float(ColKey column_key, size_t* resultcount = nullptr) const; + float maximum_float(ColKey column_key, ObjKey* return_ndx = nullptr) const; + float minimum_float(ColKey column_key, ObjKey* return_ndx = nullptr) const; + double sum_double(ColKey column_key) const; + double average_double(ColKey column_key, size_t* resultcount = nullptr) const; + double maximum_double(ColKey column_key, ObjKey* return_ndx = nullptr) const; + double minimum_double(ColKey column_key, ObjKey* return_ndx = nullptr) const; + Timestamp maximum_timestamp(ColKey column_key, ObjKey* return_ndx = nullptr); + Timestamp minimum_timestamp(ColKey column_key, ObjKey* return_ndx = nullptr); + + // 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 + + ConstTableRef& get_table() + { + return m_table; + } + + void get_outside_versions(TableVersions&) const; + + // 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 empty vector if the query is not associated with a table. + TableVersions sync_view_if_needed() const; + + std::string validate(); + + std::string get_description() const; + std::string get_description(util::serializer::SerialisationState& state) const; + + bool eval_object(ConstObj& obj) const; + +private: + void create(); + + void init() const; + size_t find_internal(size_t start = 0, size_t end = size_t(-1)) const; + void handle_pending_not(); + void set_table(TableRef tr); +public: + std::unique_ptr clone_for_handover(Transaction* tr, PayloadPolicy policy) const + { + return std::make_unique(this, tr, policy); + } + + Query(const Query* source, Transaction* tr, PayloadPolicy policy); + Query(const Query& source, Transaction* tr, PayloadPolicy policy) + : Query(&source, tr, policy) + { + } + +private: + void add_expression_node(std::unique_ptr); + + template + Query& equal(ColKey column_key1, ColKey column_key2); + + template + Query& less(ColKey column_key1, ColKey column_key2); + + template + Query& less_equal(ColKey column_key1, ColKey column_key2); + + template + Query& greater(ColKey column_key1, ColKey column_key2); + + template + Query& greater_equal(ColKey column_key1, ColKey column_key2); + + template + Query& not_equal(ColKey column_key1, ColKey column_key2); + + template + Query& add_condition(ColKey column_key, T value); + + template + Query& add_size_condition(ColKey column_key, int64_t value); + + template + double average(ColKey column_key, size_t* resultcount = nullptr) const; + + template + R aggregate(ColKey column_key, size_t* resultcount = nullptr, ObjKey* return_ndx = nullptr) const; + + size_t find_best_node(ParentNode* pn) const; + void aggregate_internal(ParentNode* pn, QueryStateBase* st, size_t start, size_t end, + ArrayPayload* source_column) const; + + void find_all(ConstTableView& tv, size_t start = 0, size_t end = size_t(-1), size_t limit = size_t(-1)) const; + size_t do_count(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 ConstTableView; + friend class SubQueryCount; + friend class metrics::QueryInfo; + + std::string error_code; + + std::vector m_groups; + mutable std::vector m_table_keys; + + TableRef m_table; + + // points to the base class of the restricting view. If the restricting + // view is a link view, m_source_link_list is non-zero. If it is a table view, + // m_source_table_view is non-zero. + ObjList* m_view = nullptr; + + // At most one of these can be non-zero, and if so the non-zero one indicates the restricting view. + LnkLstPtr m_source_link_list; // link lists are owned by the query. + ConstTableView* 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(ColKey column_key, const char* c_str, bool case_sensitive) +{ + return equal(column_key, StringData(c_str), case_sensitive); +} + +inline Query& Query::not_equal(ColKey column_key, const char* c_str, bool case_sensitive) +{ + return not_equal(column_key, StringData(c_str), case_sensitive); +} + +} // namespace realm + +#endif // REALM_QUERY_HPP diff --git a/src/vendor-include/realm-ios/include/realm/query_conditions.hpp b/src/vendor-include/realm-ios/include/realm/query_conditions.hpp new file mode 100644 index 000000000..bddc8b70e --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/query_conditions.hpp @@ -0,0 +1,914 @@ +/************************************************************************* + * + * 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 { + +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 +}; + +class ClusterKeyArray; + +class QueryStateBase { +public: + size_t m_match_count; + size_t m_limit; + int64_t m_minmax_index; // used only for min/max, to save index of current min/max value + uint64_t m_key_offset; + const ClusterKeyArray* m_key_values; + QueryStateBase(size_t limit) + : m_match_count(0) + , m_limit(limit) + , m_minmax_index(-1) + , m_key_offset(0) + , m_key_values(nullptr) + { + } + virtual ~QueryStateBase() + { + } + +private: + virtual void dyncast(); +}; + +template +class QueryState; + + +// 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 std::string description() + { + return "CONTAINS"; + } + + 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()(BinaryData b1, const char*, const char*, BinaryData b2, bool = false, bool = false) const + { + StringData s1(b1.data(), b1.size()); + StringData s2(b2.data(), b2.size()); + return s2.like(s1); + } + bool operator()(StringData v1, StringData v2, bool = false, bool = false) const + { + return v2.like(v1); + } + bool operator()(BinaryData b1, BinaryData b2, bool = false, bool = false) const + { + StringData s1(b1.data(), b1.size()); + StringData s2(b2.data(), b2.size()); + return s2.like(s1); + } + + 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 std::string description() + { + return "LIKE"; + } + + 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 std::string description() + { + return "BEGINSWITH"; + } + + 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 std::string description() + { + return "ENDSWITH"; + } + + 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); + } + + static std::string description() + { + return "=="; + } +}; + +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 = delete; + + static std::string description() + { + return "!="; + } +}; + +// 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(); + } + bool operator()(BinaryData b1, BinaryData b2, bool = false, bool = false) const + { + StringData s1(b1.data(), b1.size()); + StringData s2(b2.data(), b2.size()); + return this->operator()(s1, s2, false, false); + } + + // 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 std::string description() + { + return "CONTAINS[c]"; + } + + 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); + } + bool operator()(BinaryData b1, const char* b1_upper, const char* b1_lower, BinaryData b2, bool = false, + bool = false) const + { + if (b2.is_null() || b1.is_null()) { + return (b2.is_null() && b1.is_null()); + } + StringData s2(b2.data(), b2.size()); + + return string_like_ins(s2, b1_lower, b1_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); + } + bool operator()(BinaryData b1, BinaryData b2, bool = false, bool = false) const + { + if (b2.is_null() || b1.is_null()) { + return (b2.is_null() && b1.is_null()); + } + StringData s1(b1.data(), b1.size()); + StringData s2(b2.data(), b2.size()); + + std::string s1_upper = case_map(s1, true, IgnoreErrors); + std::string s1_lower = case_map(s1, false, IgnoreErrors); + return string_like_ins(s2, s1_lower, s1_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 std::string description() + { + return "LIKE[c]"; + } + + 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()); + } + bool operator()(BinaryData b1, BinaryData b2, bool = false, bool = false) const + { + StringData s1(b1.data(), b1.size()); + StringData s2(b2.data(), b2.size()); + return this->operator()(s1, s2, false, 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 std::string description() + { + return "BEGINSWITH[c]"; + } + + 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()); + } + bool operator()(BinaryData b1, BinaryData b2, bool = false, bool = false) const + { + StringData s1(b1.data(), b1.size()); + StringData s2(b2.data(), b2.size()); + return this->operator()(s1, s2, false, 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 std::string description() + { + return "ENDSWITH[c]"; + } + + 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()); + } + bool operator()(BinaryData b1, BinaryData b2, bool = false, bool = false) const + { + StringData s1(b1.data(), b1.size()); + StringData s2(b2.data(), b2.size()); + return this->operator()(s1, s2, false, 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 std::string description() + { + return "==[c]"; + } + + 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()); + } + bool operator()(BinaryData b1, BinaryData b2, bool = false, bool = false) const + { + StringData s1(b1.data(), b1.size()); + StringData s2(b2.data(), b2.size()); + return this->operator()(s1, s2, false, 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; + } + + static std::string description() + { + return "!=[c]"; + } + + 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; + } + + static std::string description() + { + return ">"; + } +}; + +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; + } + + static std::string description() + { + return "none"; + } +}; + +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; + } + static std::string description() + { + return "!= NULL"; + } +}; + + +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; + } + static std::string description() + { + return "<"; + } +}; + +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); + } + bool operator()(const util::Optional& v1, const util::Optional& v2, bool v1null, bool v2null) const + { + if (v1null && v2null) + return false; + + return (!v1null && !v2null && v1.value() <= v2.value()); + } + template + bool operator()(A, B, C, D) const + { + REALM_ASSERT(false); + return false; + } + static std::string description() + { + return "<="; + } + 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); + } + bool operator()(const util::Optional& v1, const util::Optional& v2, bool v1null, bool v2null) const + { + if (v1null && v2null) + return false; + + return (!v1null && !v2null && v1.value() >= v2.value()); + } + template + bool operator()(A, B, C, D) const + { + REALM_ASSERT(false); + return false; + } + static std::string description() + { + return ">="; + } + 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/src/vendor-include/realm-ios/include/realm/query_engine.hpp b/src/vendor-include/realm-ios/include/realm/query_engine.hpp new file mode 100644 index 000000000..38f459e68 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/query_engine.hpp @@ -0,0 +1,2374 @@ +/************************************************************************* + * + * 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 + +#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); +using Evaluator = util::FunctionRef; + +class ParentNode { + typedef ParentNode ThisType; + +public: + ParentNode() = default; + virtual ~ParentNode() = default; + + virtual bool has_search_index() const + { + return false; + } + virtual void index_based_aggregate(size_t, Evaluator) {} + + 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); + + bool match(ConstObj& obj); + + virtual void init() + { + if (m_child) + m_child->init(); + + m_column_action_specializer = nullptr; + } + + void get_link_dependencies(std::vector& tables) const + { + collect_dependencies(tables); + if (m_child) + m_child->get_link_dependencies(tables); + } + + void set_table(ConstTableRef table) + { + if (table == m_table) + return; + + m_table = table; + if (m_condition_column_key != ColKey()) { + m_condition_column_name = m_table->get_column_name(m_condition_column_key); + } + if (m_child) + m_child->set_table(table); + table_changed(); + } + + void set_cluster(const Cluster* cluster) + { + m_cluster = cluster; + if (m_child) + m_child->set_cluster(cluster); + cluster_changed(); + } + + virtual void collect_dependencies(std::vector&) const + { + } + + 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, ArrayPayload* source_column, size_t r) + { + // TResult: type of query result + // TSourceValue: type of aggregate source + using TSourceValue = typename LeafType::value_type; + using TResult = typename AggregateResultType::result_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(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, + ArrayPayload* 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, Transaction* tr); + + 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(Transaction* = nullptr) const = 0; + + ColKey get_column_key(StringData column_name) const + { + ColKey column_key; + if (column_name.size() > 0) { + column_key = m_table.unchecked_ptr()->get_column_key(column_name); + if (column_key == ColKey()) { + throw LogicError(LogicError::column_does_not_exist); + } + } + return column_key; + } + + virtual std::string describe(util::serializer::SerialisationState&) const + { + return ""; + } + + virtual std::string describe_condition() const + { + return "matches"; + } + + virtual std::string describe_expression(util::serializer::SerialisationState& state) const + { + std::string s; + s = describe(state); + if (m_child) { + s = s + " and " + m_child->describe_expression(state); + } + return s; + } + + std::unique_ptr m_child; + std::vector m_children; + std::string m_condition_column_name; + mutable ColKey m_condition_column_key = ColKey(); // 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*, ArrayPayload*, size_t); + Column_action_specialized m_column_action_specializer = nullptr; + ConstTableRef m_table = ConstTableRef(); + const Cluster* m_cluster = nullptr; + QueryStateBase* m_state = nullptr; + std::string error_code; + + ColumnType get_real_column_type(ColKey key) + { + return m_table.unchecked_ptr()->get_real_column_type(key); + } + +private: + virtual void table_changed() + { + } + virtual void cluster_changed() + { + // TODO: Should eventually be pure + } +}; + + +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(ColKey column_key) + { + m_condition_column_key = column_key; + } + + ColumnNodeBase(const ColumnNodeBase& from, Transaction* tr) + : ParentNode(from, tr) + , 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 LeafType::value_type; + using ResultType = typename AggregateResultType::result_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(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; + // Column of values used in aggregate (act_FindAll, actReturnFirst, act_Sum, etc) + ArrayPayload* m_source_column = nullptr; +}; + +template +class IntegerNodeBase : public ColumnNodeBase { + using ThisType = IntegerNodeBase; + +public: + using TConditionValue = typename LeafType::value_type; + // static const bool nullable = ColType::nullable; + + template + bool find_callback_specialization(size_t start_in_leaf, size_t end_in_leaf) + { + using AggregateLeafType = typename GetLeafType::type; + auto cb = std::bind(std::mem_fn(&ThisType::template match_callback), this, + std::placeholders::_1); + return this->m_leaf_ptr->template find(m_value, start_in_leaf, + end_in_leaf, 0, nullptr, cb); + } + +protected: + size_t aggregate_local_impl(QueryStateBase* st, size_t start, size_t end, size_t local_limit, + ArrayPayload* source_column, int c) + { + m_table.check(); + REALM_ASSERT(m_cluster); + 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); + if (fastmode) { + bool cont; + cont = m_leaf_ptr->find(c, m_action, m_value, start, end, 0, 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)(start, end); + if (!cont) + return not_found; + } + + 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, ColKey column_key) + : ColumnNodeBase(column_key) + , m_value(std::move(value)) + { + } + + IntegerNodeBase(const ThisType& from, Transaction* tr) + : ColumnNodeBase(from, tr) + , m_value(from.m_value) + , m_find_callback_specialized(from.m_find_callback_specialized) + { + } + + void cluster_changed() override + { + // Assigning nullptr will cause the Leaf destructor to be called. Must + // be done before assigning a new one. Otherwise the destructor will be + // called after the constructor is called and that is unfortunate if + // the object has the same address. (As in this case) + m_array_ptr = nullptr; + // Create new Leaf + m_array_ptr = LeafPtr(new (&m_leaf_cache_storage) LeafType(m_table.unchecked_ptr()->get_alloc())); + m_cluster->init_leaf(this->m_condition_column_key, m_array_ptr.get()); + m_leaf_ptr = m_array_ptr.get(); + } + + void init() override + { + ColumnNodeBase::init(); + + m_dT = _impl::CostHeuristic::dT(); + m_dD = _impl::CostHeuristic::dD(); + } + + bool should_run_in_fastmode(ArrayPayload* source_leaf) const + { + if (m_children.size() > 1 || m_fastmode_disabled) + return false; + if (source_leaf == nullptr) + return true; + // Compare leafs to see if they are the same + auto leaf = dynamic_cast(source_leaf); + return leaf ? leaf->get_ref() == m_leaf_ptr->get_ref() : false; + } + + // Search value: + TConditionValue m_value; + + // Leaf cache + using LeafCacheStorage = typename std::aligned_storage::type; + using LeafPtr = std::unique_ptr; + LeafCacheStorage m_leaf_cache_storage; + LeafPtr m_array_ptr; + const LeafType* m_leaf_ptr = nullptr; + + // Aggregate optimization + using TFind_callback_specialized = bool (ThisType::*)(size_t, size_t); + TFind_callback_specialized m_find_callback_specialized = nullptr; + + template + static TFind_callback_specialized get_specialized_callback(Action action, DataType col_id, bool is_nullable) + { + switch (action) { + case act_Count: + return get_specialized_callback_2_int(col_id, is_nullable); + case act_Sum: + return get_specialized_callback_2(col_id, is_nullable); + case act_Max: + return get_specialized_callback_2(col_id, is_nullable); + case act_Min: + return get_specialized_callback_2(col_id, is_nullable); + case act_FindAll: + return get_specialized_callback_2_int(col_id, is_nullable); + case act_CallbackIdx: + return get_specialized_callback_2_int(col_id, is_nullable); + default: + break; + } + REALM_ASSERT(false); // Invalid aggregate function + return nullptr; + } + + template + static TFind_callback_specialized get_specialized_callback_2(DataType col_id, bool is_nullable) + { + switch (col_id) { + case type_Int: + return get_specialized_callback_3(is_nullable); + case type_Float: + return get_specialized_callback_3(is_nullable); + case type_Double: + return get_specialized_callback_3(is_nullable); + case type_Timestamp: + return get_specialized_callback_3(is_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 is_nullable) + { + if (col_id == type_Int) { + return get_specialized_callback_3(is_nullable); + } + REALM_ASSERT(false); // Invalid aggregate source column + return nullptr; + } + + template + static TFind_callback_specialized get_specialized_callback_3(bool is_nullable) + { + if (is_nullable) { + return &IntegerNodeBase::template find_callback_specialization; + } + else { + return &IntegerNodeBase::template find_callback_specialization; + } + } +}; + + +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, ColKey column_key) + : BaseType(value, column_key) + { + } + IntegerNode(const IntegerNode& from, Transaction* tr) + : BaseType(from, tr) + { + } + + void aggregate_local_prepare(Action action, DataType col_id, bool is_nullable) override + { + this->m_fastmode_disabled = (col_id == type_Float || col_id == type_Double); + this->m_action = action; + this->m_find_callback_specialized = + IntegerNodeBase::template get_specialized_callback(action, col_id, + is_nullable); + } + + size_t aggregate_local(QueryStateBase* st, size_t start, size_t end, size_t local_limit, + ArrayPayload* 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 + { + return this->m_leaf_ptr->template find_first(this->m_value, start, end); + } + + std::string describe(util::serializer::SerialisationState& state) const override + { + return state.describe_column(ParentNode::m_table, ColumnNodeBase::m_condition_column_key) + " " + + describe_condition() + " " + util::serializer::print_value(this->m_value); + } + + std::string describe_condition() const override + { + return TConditionFunction::description(); + } + + std::unique_ptr clone(Transaction* tr) const override + { + return std::unique_ptr(new ThisType(*this, tr)); + } +}; + +template +class IntegerNode : public IntegerNodeBase { +public: + using BaseType = IntegerNodeBase; + using TConditionValue = typename BaseType::TConditionValue; + using ThisType = IntegerNode; + + IntegerNode(TConditionValue value, ColKey column_key) + : BaseType(value, column_key) + { + } + ~IntegerNode() + { + } + + void init() override + { + BaseType::init(); + m_nb_needles = m_needles.size(); + + if (has_search_index()) { + // _search_index_init(); + m_result.clear(); + auto index = ParentNode::m_table->get_search_index(ParentNode::m_condition_column_key); + index->find_all(m_result, BaseType::m_value); + m_result_get = 0; + m_last_start_key = ObjKey(); + IntegerNodeBase::m_dT = 0; + } + } + + void consume_condition(IntegerNode* other) + { + REALM_ASSERT(this->m_condition_column_key == other->m_condition_column_key); + REALM_ASSERT(other->m_needles.empty()); + if (m_needles.empty()) { + m_needles.insert(this->m_value); + } + m_needles.insert(other->m_value); + } + + bool has_search_index() const override + { + return this->m_table->has_search_index(IntegerNodeBase::m_condition_column_key); + } + + void index_based_aggregate(size_t limit, Evaluator evaluator) override + { + for (size_t t = 0; t < m_result.size() && limit > 0; ++t) { + auto obj = this->m_table->get_object(m_result[t]); + if (evaluator(obj)) { + --limit; + } + } + } + + void aggregate_local_prepare(Action action, DataType col_id, bool is_nullable) override + { + this->m_fastmode_disabled = (col_id == type_Float || col_id == type_Double); + this->m_action = action; + this->m_find_callback_specialized = + IntegerNodeBase::template get_specialized_callback(action, col_id, is_nullable); + } + + size_t aggregate_local(QueryStateBase* st, size_t start, size_t end, size_t local_limit, + ArrayPayload* source_column) override + { + constexpr int cond = Equal::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); + size_t s = realm::npos; + + if (start < end) { + if (has_search_index()) { + ObjKey first_key = BaseType::m_cluster->get_real_key(start); + if (first_key < m_last_start_key) { + // We are not advancing through the clusters. We basically don't know where we are, + // so just start over from the beginning. + auto it = std::lower_bound(m_result.begin(), m_result.end(), first_key); + m_result_get = (it == m_result.end()) ? realm::npos : (it - m_result.begin()); + } + m_last_start_key = first_key; + + if (m_result_get < m_result.size()) { + auto actual_key = m_result[m_result_get]; + // skip through keys which are in "earlier" leafs than the one selected by start..end: + while (first_key > actual_key) { + m_result_get++; + if (m_result_get == m_result.size()) + return not_found; + actual_key = m_result[m_result_get]; + } + + // if actual key is bigger than last key, it is not in this leaf + ObjKey last_key = BaseType::m_cluster->get_real_key(end - 1); + if (actual_key > last_key) + return not_found; + + // key is known to be in this leaf, so find key whithin leaf keys + return BaseType::m_cluster->lower_bound_key( + ObjKey(actual_key.value - BaseType::m_cluster->get_offset())); + } + return not_found; + } + + if (m_nb_needles) { + s = find_first_haystack(start, end); + } + else if (end - start == 1) { + if (this->m_leaf_ptr->get(start) == this->m_value) { + s = start; + } + } + else { + s = this->m_leaf_ptr->template find_first(this->m_value, start, end); + } + } + + return s; + } + + std::string describe(util::serializer::SerialisationState& state) const override + { + REALM_ASSERT(this->m_condition_column_key); + std::string col_descr = state.describe_column(this->m_table, this->m_condition_column_key); + + if (m_needles.empty()) { + return col_descr + " " + Equal::description() + " " + + util::serializer::print_value(IntegerNodeBase::m_value); + } + + // FIXME: once the parser supports it, print something like "column IN {n1, n2, n3}" + std::string desc = "("; + bool is_first = true; + for (auto it : m_needles) { + if (!is_first) + desc += " or "; + desc += + col_descr + " " + Equal::description() + " " + util::serializer::print_value(it); // "it" may be null + is_first = false; + } + desc += ")"; + return desc; + } + + std::unique_ptr clone(Transaction* tr) const override + { + return std::unique_ptr(new ThisType(*this, tr)); + } + +private: + std::unordered_set m_needles; + std::vector m_result; + size_t m_nb_needles = 0; + size_t m_result_get = 0; + ObjKey m_last_start_key; + + IntegerNode(const IntegerNode& from, Transaction* patches) + : BaseType(from, patches) + , m_needles(from.m_needles) + { + } + size_t find_first_haystack(size_t start, size_t end) + { + const auto not_in_set = m_needles.end(); + // for a small number of conditions, it is faster to do a linear search than to compute the hash + // the decision threshold was determined experimentally to be 22 conditions + bool search = m_nb_needles < 22; + auto cmp_fn = [this, search, not_in_set](const auto& v) { + if (search) { + for (auto it = m_needles.begin(); it != not_in_set; ++it) { + if (*it == v) + return true; + } + return false; + } + else { + return (m_needles.find(v) != not_in_set); + } + }; + for (size_t i = start; i < end; ++i) { + auto val = this->m_leaf_ptr->get(i); + if (cmp_fn(val)) { + return i; + } + } + return realm::npos; + } +}; + + +// This node is currently used for floats and doubles only +template +class FloatDoubleNode : public ParentNode { +public: + using TConditionValue = typename LeafType::value_type; + static const bool special_null_node = false; + + FloatDoubleNode(TConditionValue v, ColKey column_key) + : m_value(v) + { + m_condition_column_key = column_key; + m_dT = 1.0; + } + FloatDoubleNode(null, ColKey column_key) + : m_value(null::get_null_float()) + { + m_condition_column_key = column_key; + m_dT = 1.0; + } + + void cluster_changed() override + { + // Assigning nullptr will cause the Leaf destructor to be called. Must + // be done before assigning a new one. Otherwise the destructor will be + // called after the constructor is called and that is unfortunate if + // the object has the same address. (As in this case) + m_array_ptr = nullptr; + // Create new Leaf + m_array_ptr = LeafPtr(new (&m_leaf_cache_storage) LeafType(m_table.unchecked_ptr()->get_alloc())); + m_cluster->init_leaf(this->m_condition_column_key, m_array_ptr.get()); + m_leaf_ptr = m_array_ptr.get(); + } + + 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_leaf_ptr->get(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_key)) + return find(true); + else + return find(false); + } + + std::string describe(util::serializer::SerialisationState& state) const override + { + REALM_ASSERT(m_condition_column_key); + return state.describe_column(ParentNode::m_table, m_condition_column_key) + " " + describe_condition() + " " + + util::serializer::print_value(FloatDoubleNode::m_value); + } + std::string describe_condition() const override + { + return TConditionFunction::description(); + } + + std::unique_ptr clone(Transaction* tr) const override + { + return std::unique_ptr(new FloatDoubleNode(*this, tr)); + } + + FloatDoubleNode(const FloatDoubleNode& from, Transaction* tr) + : ParentNode(from, tr) + , m_value(from.m_value) + { + } + +protected: + TConditionValue m_value; + // Leaf cache + using LeafCacheStorage = typename std::aligned_storage::type; + using LeafPtr = std::unique_ptr; + LeafCacheStorage m_leaf_cache_storage; + LeafPtr m_array_ptr; + const LeafType* m_leaf_ptr = nullptr; +}; + +template +class SizeNode : public ParentNode { +public: + SizeNode(int64_t v, ColKey column) + : m_value(v) + { + m_condition_column_key = column; + } + + void cluster_changed() override + { + // Assigning nullptr will cause the Leaf destructor to be called. Must + // be done before assigning a new one. Otherwise the destructor will be + // called after the constructor is called and that is unfortunate if + // the object has the same address. (As in this case) + m_array_ptr = nullptr; + m_array_ptr = LeafPtr(new (&m_leaf_cache_storage) LeafType(m_table.unchecked_ptr()->get_alloc())); + m_cluster->init_leaf(this->m_condition_column_key, m_array_ptr.get()); + m_leaf_ptr = m_array_ptr.get(); + } + + 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) { + T v = m_leaf_ptr->get(s); + if (v) { + int64_t sz = v.size(); + if (TConditionFunction()(sz, m_value)) + return s; + } + } + return not_found; + } + + std::unique_ptr clone(Transaction* tr) const override + { + return std::unique_ptr(new SizeNode(*this, tr)); + } + + SizeNode(const SizeNode& from, Transaction* tr) + : ParentNode(from, tr) + , m_value(from.m_value) + { + } + +private: + // Leaf cache + using LeafType = typename ColumnTypeTraits::cluster_leaf_type; + using LeafCacheStorage = typename std::aligned_storage::type; + using LeafPtr = std::unique_ptr; + LeafCacheStorage m_leaf_cache_storage; + LeafPtr m_array_ptr; + const LeafType* m_leaf_ptr = nullptr; + + int64_t m_value; +}; + + +template +class SizeListNode : public ParentNode { +public: + SizeListNode(int64_t v, ColKey column) + : m_value(v) + { + m_condition_column_key = column; + } + + void cluster_changed() override + { + // Assigning nullptr will cause the Leaf destructor to be called. Must + // be done before assigning a new one. Otherwise the destructor will be + // called after the constructor is called and that is unfortunate if + // the object has the same address. (As in this case) + m_array_ptr = nullptr; + m_array_ptr = LeafPtr(new (&m_leaf_cache_storage) ArrayList(m_table.unchecked_ptr()->get_alloc())); + m_cluster->init_leaf(this->m_condition_column_key, m_array_ptr.get()); + m_leaf_ptr = m_array_ptr.get(); + } + + void init() override + { + ParentNode::init(); + m_dD = 50.0; + } + + size_t find_first_local(size_t start, size_t end) override + { + for (size_t s = start; s < end; ++s) { + ref_type ref = m_leaf_ptr->get(s); + if (ref) { + ListType list(m_table.unchecked_ptr()->get_alloc()); + list.init_from_ref(ref); + int64_t sz = list.size(); + if (TConditionFunction()(sz, m_value)) + return s; + } + } + return not_found; + } + + std::unique_ptr clone(Transaction* tr) const override + { + return std::unique_ptr(new SizeListNode(*this, tr)); + } + + SizeListNode(const SizeListNode& from, Transaction* tr) + : ParentNode(from, tr) + , m_value(from.m_value) + { + } + +private: + // Leaf cache + using ListType = BPlusTree; + using LeafCacheStorage = typename std::aligned_storage::type; + using LeafPtr = std::unique_ptr; + LeafCacheStorage m_leaf_cache_storage; + LeafPtr m_array_ptr; + const ArrayList* m_leaf_ptr = nullptr; + + int64_t m_value; +}; + + +template +class BinaryNode : public ParentNode { +public: + using TConditionValue = BinaryData; + static const bool special_null_node = false; + + BinaryNode(BinaryData v, ColKey column) + : m_value(v) + { + m_dT = 100.0; + m_condition_column_key = column; + } + + BinaryNode(null, ColKey column) + : BinaryNode(BinaryData{}, column) + { + } + + void cluster_changed() override + { + m_array_ptr = nullptr; + m_array_ptr = LeafPtr(new (&m_leaf_cache_storage) ArrayBinary(m_table.unchecked_ptr()->get_alloc())); + m_cluster->init_leaf(this->m_condition_column_key, m_array_ptr.get()); + m_leaf_ptr = m_array_ptr.get(); + } + + 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_leaf_ptr->get(s); + if (condition(m_value.get(), value)) + return s; + } + return not_found; + } + + virtual std::string describe(util::serializer::SerialisationState& state) const override + { + REALM_ASSERT(m_condition_column_key); + return state.describe_column(ParentNode::m_table, m_condition_column_key) + " " + + TConditionFunction::description() + " " + util::serializer::print_value(BinaryNode::m_value.get()); + } + + std::unique_ptr clone(Transaction* tr) const override + { + return std::unique_ptr(new BinaryNode(*this, tr)); + } + + BinaryNode(const BinaryNode& from, Transaction* tr) + : ParentNode(from, tr) + , m_value(from.m_value) + { + } + +private: + OwnedBinaryData m_value; + using LeafCacheStorage = typename std::aligned_storage::type; + using LeafPtr = std::unique_ptr; + LeafCacheStorage m_leaf_cache_storage; + LeafPtr m_array_ptr; + const ArrayBinary* m_leaf_ptr = nullptr; +}; + +template +class BoolNode : public ParentNode { +public: + using TConditionValue = bool; + + BoolNode(util::Optional v, ColKey column) + : m_value(v) + { + m_condition_column_key = column; + } + + BoolNode(const BoolNode& from, Transaction* tr) + : ParentNode(from, tr) + , m_value(from.m_value) + { + } + + void cluster_changed() override + { + m_array_ptr = nullptr; + m_array_ptr = LeafPtr(new (&m_leaf_cache_storage) ArrayBoolNull(m_table.unchecked_ptr()->get_alloc())); + m_cluster->init_leaf(this->m_condition_column_key, m_array_ptr.get()); + m_leaf_ptr = m_array_ptr.get(); + } + + void init() override + { + ParentNode::init(); + + m_dD = 100.0; + } + + size_t find_first_local(size_t start, size_t end) override + { + TConditionFunction condition; + bool m_value_is_null = !m_value; + for (size_t s = start; s < end; ++s) { + util::Optional value = m_leaf_ptr->get(s); + if (condition(value, m_value, !value, m_value_is_null)) + return s; + } + return not_found; + } + + virtual std::string describe(util::serializer::SerialisationState& state) const override + { + return state.describe_column(ParentNode::m_table, m_condition_column_key) + " " + + TConditionFunction::description() + " " + util::serializer::print_value(m_value); + } + + std::unique_ptr clone(Transaction* tr) const override + { + return std::unique_ptr(new BoolNode(*this, tr)); + } + +private: + util::Optional m_value; + using LeafCacheStorage = typename std::aligned_storage::type; + using LeafPtr = std::unique_ptr; + LeafCacheStorage m_leaf_cache_storage; + LeafPtr m_array_ptr; + const ArrayBoolNull* m_leaf_ptr = nullptr; +}; + +class TimestampNodeBase : public ParentNode { +public: + using TConditionValue = Timestamp; + static const bool special_null_node = false; + + TimestampNodeBase(Timestamp v, ColKey column) + : m_value(v) + { + m_condition_column_key = column; + } + + TimestampNodeBase(null, ColKey column) + : TimestampNodeBase(Timestamp{}, column) + { + } + + void cluster_changed() override + { + m_array_ptr = nullptr; + m_array_ptr = LeafPtr(new (&m_leaf_cache_storage) ArrayTimestamp(m_table.unchecked_ptr()->get_alloc())); + m_cluster->init_leaf(this->m_condition_column_key, m_array_ptr.get()); + m_leaf_ptr = m_array_ptr.get(); + } + + void init() override + { + ParentNode::init(); + + m_dD = 100.0; + } + +protected: + TimestampNodeBase(const TimestampNodeBase& from, Transaction* tr) + : ParentNode(from, tr) + , m_value(from.m_value) + { + } + + Timestamp m_value; + using LeafCacheStorage = typename std::aligned_storage::type; + using LeafPtr = std::unique_ptr; + LeafCacheStorage m_leaf_cache_storage; + LeafPtr m_array_ptr; + const ArrayTimestamp* m_leaf_ptr = nullptr; +}; + +template +class TimestampNode : public TimestampNodeBase { +public: + using TimestampNodeBase::TimestampNodeBase; + + size_t find_first_local(size_t start, size_t end) override + { + return m_leaf_ptr->find_first(m_value, start, end); + } + + std::string describe(util::serializer::SerialisationState& state) const override + { + REALM_ASSERT(m_condition_column_key); + return state.describe_column(ParentNode::m_table, m_condition_column_key) + " " + + TConditionFunction::description() + " " + util::serializer::print_value(TimestampNode::m_value); + } + + std::unique_ptr clone(Transaction* tr) const override + { + return std::unique_ptr(new TimestampNode(*this, tr)); + } +}; + +class StringNodeBase : public ParentNode { +public: + using TConditionValue = StringData; + static const bool special_null_node = true; + + StringNodeBase(StringData v, ColKey column) + : m_value(v.is_null() ? util::none : util::make_optional(std::string(v))) + { + m_condition_column_key = column; + } + + void table_changed() override + { + m_is_string_enum = m_table.unchecked_ptr()->is_enumerated(m_condition_column_key); + } + + void cluster_changed() override + { + // Assigning nullptr will cause the Leaf destructor to be called. Must + // be done before assigning a new one. Otherwise the destructor will be + // called after the constructor is called and that is unfortunate if + // the object has the same address. (As in this case) + m_array_ptr = nullptr; + // Create new Leaf + m_array_ptr = LeafPtr(new (&m_leaf_cache_storage) ArrayString(m_table.unchecked_ptr()->get_alloc())); + m_cluster->init_leaf(this->m_condition_column_key, m_array_ptr.get()); + m_leaf_ptr = m_array_ptr.get(); + } + + 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; + } + + virtual void clear_leaf_state() + { + m_array_ptr = nullptr; + } + + StringNodeBase(const StringNodeBase& from, Transaction* tr) + : ParentNode(from, tr) + , m_value(from.m_value) + , m_is_string_enum(from.m_is_string_enum) + { + } + + virtual std::string describe(util::serializer::SerialisationState& state) const override + { + REALM_ASSERT(m_condition_column_key); + StringData sd; + if (bool(StringNodeBase::m_value)) { + sd = StringData(StringNodeBase::m_value.value()); + } + return state.describe_column(ParentNode::m_table, m_condition_column_key) + " " + describe_condition() + " " + + util::serializer::print_value(sd); + } + +protected: + util::Optional m_value; + + using LeafCacheStorage = typename std::aligned_storage::type; + using LeafPtr = std::unique_ptr; + LeafCacheStorage m_leaf_cache_storage; + LeafPtr m_array_ptr; + const ArrayString* m_leaf_ptr = nullptr; + + bool m_is_string_enum = false; + + 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) + { + return m_leaf_ptr->get(s); + } +}; + +// Conditions for strings. Note that Equal is specialized later in this file! +template +class StringNode : public StringNodeBase { +public: + StringNode(StringData v, ColKey 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.c_str(), m_lcase.c_str(), t)) + return s; + } + return not_found; + } + + virtual std::string describe_condition() const override + { + return TConditionFunction::description(); + } + + std::unique_ptr clone(Transaction* tr) const override + { + return std::unique_ptr(new StringNode(*this, tr)); + } + + StringNode(const StringNode& from, Transaction* tr) + : StringNodeBase(from, tr) + , 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, ColKey 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; + } + + virtual std::string describe_condition() const override + { + return Contains::description(); + } + + + std::unique_ptr clone(Transaction* tr) const override + { + return std::unique_ptr(new StringNode(*this, tr)); + } + + StringNode(const StringNode& from, Transaction* tr) + : StringNodeBase(from, tr) + , 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, ColKey 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); + // The current behaviour is to return all results when querying for a null string. + // See comment above Query_NextGen_StringConditions on why every string including "" contains null. + if (!bool(m_value)) { + return s; + } + if (cond(StringData(m_value), m_ucase.c_str(), m_lcase.c_str(), m_charmap, t)) + return s; + } + return not_found; + } + + virtual std::string describe_condition() const override + { + return ContainsIns::description(); + } + + std::unique_ptr clone(Transaction* tr) const override + { + return std::unique_ptr(new StringNode(*this, tr)); + } + + StringNode(const StringNode& from, Transaction* tr) + : StringNodeBase(from, tr) + , 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, ColKey column) + : StringNodeBase(v, column) + { + } + StringNodeEqualBase(const StringNodeEqualBase& from, Transaction* tr) + : StringNodeBase(from, tr) + , m_has_search_index(from.m_has_search_index) + { + } + + void init() override; + + bool has_search_index() const override + { + return m_has_search_index; + } + + void cluster_changed() override + { + // If we use searchindex, we do not need further access to clusters + if (!m_has_search_index) { + StringNodeBase::cluster_changed(); + } + } + + + size_t find_first_local(size_t start, size_t end) override; + + virtual std::string describe_condition() const override + { + return Equal::description(); + } + +protected: + ObjKey m_actual_key; + ObjKey m_last_start_key; + size_t m_results_start; + size_t m_results_ndx; + size_t m_results_end; + bool m_has_search_index = false; + + inline BinaryData str_to_bin(const StringData& s) noexcept + { + return BinaryData(s.data(), s.size()); + } + + virtual ObjKey get_key(size_t ndx) = 0; + virtual void _search_index_init() = 0; + virtual size_t _find_first_local(size_t start, size_t end) = 0; +}; + +// Specialization for Equal condition on Strings - we specialize because we can utilize indexes (if they exist) for +// Equal. This specialisation also supports combining other StringNode conditions into itself in order to +// optimise the non-indexed linear search that can be happen when many conditions are OR'd together in an "IN" query. +// Future optimization: make specialization for greater, notequal, etc +template <> +class StringNode : public StringNodeEqualBase { +public: + using StringNodeEqualBase::StringNodeEqualBase; + + void table_changed() override + { + StringNodeBase::table_changed(); + m_has_search_index = m_table.unchecked_ptr()->has_search_index(m_condition_column_key) || + m_table.unchecked_ptr()->get_primary_key_column() == m_condition_column_key; + } + + void _search_index_init() override; + + void consume_condition(StringNode* other); + + std::unique_ptr clone(Transaction* tr) const override + { + return std::unique_ptr(new StringNode(*this, tr)); + } + + std::string describe(util::serializer::SerialisationState& state) const override; + + StringNode(const StringNode& from, Transaction* tr) + : StringNodeEqualBase(from, tr) + { + for (auto it = from.m_needles.begin(); it != from.m_needles.end(); ++it) { + if (it->data() == nullptr && it->size() == 0) { + m_needles.insert(StringData()); // nulls + } + else { + m_needle_storage.emplace_back(StringBuffer()); + m_needle_storage.back().append(it->data(), it->size()); + m_needles.insert(StringData(m_needle_storage.back().data(), m_needle_storage.back().size())); + } + } + } + void index_based_aggregate(size_t limit, Evaluator evaluator) override + { + if (limit == 0) + return; + if (m_index_matches == nullptr) { + if (m_results_end) { // 1 result + auto obj = m_table->get_object(m_actual_key); + evaluator(obj); + } + } + else { // multiple results + for (size_t t = m_results_start; t < m_results_end && limit > 0; ++t) { + auto obj = m_table->get_object(ObjKey(m_index_matches->get(t))); + if (evaluator(obj)) { + --limit; + } + } + } + } + +private: + std::unique_ptr m_index_matches; + + ObjKey get_key(size_t ndx) override + { + if (IntegerColumn* vec = m_index_matches.get()) { + return ObjKey(vec->get(ndx)); + } + else if (m_results_end == 1) { + return m_actual_key; + } + return ObjKey(); + } + + size_t _find_first_local(size_t start, size_t end) override; + std::unordered_set m_needles; + std::vector m_needle_storage; +}; + + +// 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, ColKey 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 clear_leaf_state() override + { + StringNodeEqualBase::clear_leaf_state(); + m_index_matches.clear(); + } + + void table_changed() override + { + StringNodeBase::table_changed(); + m_has_search_index = m_table.unchecked_ptr()->has_search_index(m_condition_column_key); + } + void _search_index_init() override; + + virtual std::string describe_condition() const override + { + return EqualIns::description(); + } + + std::unique_ptr clone(Transaction* tr) const override + { + return std::unique_ptr(new StringNode(*this, tr)); + } + + StringNode(const StringNode& from, Transaction* tr) + : StringNodeEqualBase(from, tr) + , m_ucase(from.m_ucase) + , m_lcase(from.m_lcase) + { + } + + void index_based_aggregate(size_t limit, Evaluator evaluator) override + { + for (size_t t = 0; t < m_index_matches.size() && limit > 0; ++t) { + auto obj = m_table->get_object(m_index_matches[t]); + if (evaluator(obj)) { + --limit; + } + } + } + +private: + // Used for index lookup + std::vector m_index_matches; + std::string m_ucase; + std::string m_lcase; + + ObjKey get_key(size_t ndx) override + { + return m_index_matches[ndx]; + } + + 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, Transaction* tr) + : ParentNode(other, tr) + { + for (const auto& condition : other.m_conditions) { + m_conditions.emplace_back(condition->clone(tr)); + } + } + + void table_changed() override + { + for (auto& condition : m_conditions) { + condition->set_table(m_table); + } + } + + void cluster_changed() override + { + for (auto& condition : m_conditions) { + condition->set_cluster(m_cluster); + } + + 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::string describe(util::serializer::SerialisationState& state) const override + { + std::string s; + for (size_t i = 0; i < m_conditions.size(); ++i) { + if (m_conditions[i]) { + s += m_conditions[i]->describe_expression(state); + if (i != m_conditions.size() - 1) { + s += " or "; + } + } + } + if (m_conditions.size() > 1) { + s = "(" + s + ")"; + } + return s; + } + + void collect_dependencies(std::vector& versions) const override + { + for (const auto& cond : m_conditions) { + cond->collect_dependencies(versions); + } + } + + void init() override + { + ParentNode::init(); + + m_dD = 10.0; + + std::sort(m_conditions.begin(), m_conditions.end(), + [](auto& a, auto& b) { return a->m_condition_column_key < b->m_condition_column_key; }); + + combine_conditions>(); + combine_conditions>(); + combine_conditions>(); + + 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(Transaction* tr) const override + { + return std::unique_ptr(new OrNode(*this, tr)); + } + + std::vector> m_conditions; + +private: + template + void combine_conditions() { + QueryNodeType* first_match = nullptr; + QueryNodeType* advance = nullptr; + auto it = m_conditions.begin(); + while (it != m_conditions.end()) { + // Only try to optimize on QueryNodeType conditions without search index + auto node = it->get(); + if ((first_match = dynamic_cast(node)) && first_match->m_child == nullptr && + !first_match->has_search_index()) { + auto col_key = first_match->m_condition_column_key; + auto next = it + 1; + while (next != m_conditions.end() && (*next)->m_condition_column_key == col_key) { + auto next_node = next->get(); + if ((advance = dynamic_cast(next_node)) && next_node->m_child == nullptr) { + first_match->consume_condition(advance); + next = m_conditions.erase(next); + } + else { + ++next; + } + } + it = next; + } + else { + ++it; + } + } + } + + // 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 cluster_changed() override + { + m_condition->set_cluster(m_cluster); + // Heuristics bookkeeping: + m_known_range_start = 0; + m_known_range_end = 0; + m_first_in_known_range = not_found; + } + + void init() override + { + ParentNode::init(); + + m_dD = 10.0; + + std::vector v; + + m_condition->init(); + v.clear(); + m_condition->gather_children(v); + } + + 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::string describe(util::serializer::SerialisationState& state) const override + { + if (m_condition) { + return "!(" + m_condition->describe_expression(state) + ")"; + } + return "!()"; + } + + void collect_dependencies(std::vector& versions) const override + { + if (m_condition) { + m_condition->collect_dependencies(versions); + } + } + + + std::unique_ptr clone(Transaction* tr) const override + { + return std::unique_ptr(new NotNode(*this, tr)); + } + + NotNode(const NotNode& from, Transaction* tr) + : ParentNode(from, tr) + , m_condition(from.m_condition ? from.m_condition->clone(tr) : 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) + { + } + + 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 LeafType::value_type; + + TwoColumnsNode(ColKey column1, ColKey column2) + { + m_dT = 100.0; + m_condition_column_key1 = column1; + m_condition_column_key2 = column2; + } + + ~TwoColumnsNode() noexcept override + { + } + + void cluster_changed() override + { + m_array_ptr1 = nullptr; + m_array_ptr1 = LeafPtr(new (&m_leaf_cache_storage1) LeafType(m_table.unchecked_ptr()->get_alloc())); + this->m_cluster->init_leaf(this->m_condition_column_key1, m_array_ptr1.get()); + m_leaf_ptr1 = m_array_ptr1.get(); + + m_array_ptr2 = nullptr; + m_array_ptr2 = LeafPtr(new (&m_leaf_cache_storage2) LeafType(m_table.unchecked_ptr()->get_alloc())); + this->m_cluster->init_leaf(this->m_condition_column_key2, m_array_ptr2.get()); + m_leaf_ptr2 = m_array_ptr2.get(); + } + + virtual std::string describe(util::serializer::SerialisationState& state) const override + { + REALM_ASSERT(m_condition_column_key1 && m_condition_column_key2); + return state.describe_column(ParentNode::m_table, m_condition_column_key1) + " " + describe_condition() + + " " + state.describe_column(ParentNode::m_table, m_condition_column_key2); + } + + virtual std::string describe_condition() const override + { + return TConditionFunction::description(); + } + + 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. + QueryState qs(act_ReturnFirst); + bool resume = m_leaf_ptr1->template compare_leafs( + m_leaf_ptr2, start, end, 0, &qs, CallbackDummy()); + + if (resume) + s = end; + else + return to_size_t(qs.m_state); + } + 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_leaf_ptr1->get(s); + TConditionValue v2 = m_leaf_ptr2->get(s); + TConditionFunction C; + + if (C(v1, v2)) + return s; + else + s++; + } + } + return not_found; + } + + std::unique_ptr clone(Transaction* tr) const override + { + return std::unique_ptr(new TwoColumnsNode(*this, tr)); + } + + TwoColumnsNode(const TwoColumnsNode& from, Transaction* tr) + : ParentNode(from, tr) + , m_condition_column_key1(from.m_condition_column_key1) + , m_condition_column_key2(from.m_condition_column_key2) + { + } + +private: + mutable ColKey m_condition_column_key1; + mutable ColKey m_condition_column_key2; + + using LeafCacheStorage = typename std::aligned_storage::type; + using LeafPtr = std::unique_ptr; + + LeafCacheStorage m_leaf_cache_storage1; + LeafPtr m_array_ptr1; + const LeafType* m_leaf_ptr1 = nullptr; + LeafCacheStorage m_leaf_cache_storage2; + LeafPtr m_array_ptr2; + const LeafType* m_leaf_ptr2 = nullptr; +}; + + +// For Next-Generation expressions like col1 / col2 + 123 > col4 * 100. +class ExpressionNode : public ParentNode { +public: + ExpressionNode(std::unique_ptr); + + void init() override; + size_t find_first_local(size_t start, size_t end) override; + + void table_changed() override; + void cluster_changed() override; + void collect_dependencies(std::vector&) const override; + + virtual std::string describe(util::serializer::SerialisationState& state) const override; + + std::unique_ptr clone(Transaction* tr) const override; + +private: + ExpressionNode(const ExpressionNode& from, Transaction* tr); + + std::unique_ptr m_expression; +}; + + +class LinksToNode : public ParentNode { +public: + LinksToNode(ColKey origin_column_key, ObjKey target_key) + : m_target_keys(1, target_key) + { + m_dD = 10.0; + m_dT = 50.0; + m_condition_column_key = origin_column_key; + } + + LinksToNode(ColKey origin_column_key, const std::vector& target_keys) + : m_target_keys(target_keys) + { + m_dD = 10.0; + m_dT = 50.0; + m_condition_column_key = origin_column_key; + } + + void table_changed() override + { + m_column_type = m_table.unchecked_ptr()->get_column_type(m_condition_column_key); + REALM_ASSERT(m_column_type == type_Link || m_column_type == type_LinkList); + } + + void cluster_changed() override + { + m_array_ptr = nullptr; + if (m_column_type == type_Link) { + m_array_ptr = LeafPtr(new (&m_storage.m_list) ArrayKey(m_table.unchecked_ptr()->get_alloc())); + } + else if (m_column_type == type_LinkList) { + m_array_ptr = LeafPtr(new (&m_storage.m_linklist) ArrayList(m_table.unchecked_ptr()->get_alloc())); + } + m_cluster->init_leaf(this->m_condition_column_key, m_array_ptr.get()); + m_leaf_ptr = m_array_ptr.get(); + } + + virtual std::string describe(util::serializer::SerialisationState& state) const override + { + REALM_ASSERT(m_condition_column_key); + if (m_target_keys.size() > 1) + throw SerialisationError("Serialising a query which links to multiple objects is currently unsupported."); + return state.describe_column(ParentNode::m_table, m_condition_column_key) + " " + describe_condition() + " " + + util::serializer::print_value(m_target_keys[0]); + } + + virtual std::string describe_condition() const override + { + return "=="; + } + + size_t find_first_local(size_t start, size_t end) override + { + if (m_column_type == type_Link) { + for (auto& key : m_target_keys) { + if (key) { + // LinkColumn stores link to row N as the integer N + 1 + auto pos = static_cast(m_leaf_ptr)->find_first(key, start, end); + if (pos != realm::npos) { + return pos; + } + } + } + } + else if (m_column_type == type_LinkList) { + ArrayKeyNonNullable arr(m_table.unchecked_ptr()->get_alloc()); + for (size_t i = start; i < end; i++) { + if (ref_type ref = static_cast(m_leaf_ptr)->get(i)) { + arr.init_from_ref(ref); + for (auto& key : m_target_keys) { + if (key) { + if (arr.find_first(key, 0, arr.size()) != not_found) + return i; + } + } + } + } + } + + return not_found; + } + + std::unique_ptr clone(Transaction*) const override + { + return std::unique_ptr(new LinksToNode(*this)); + } + +private: + std::vector m_target_keys; + DataType m_column_type = type_Link; + using LeafPtr = std::unique_ptr; + union Storage { + typename std::aligned_storage::type m_list; + typename std::aligned_storage::type m_linklist; + }; + Storage m_storage; + LeafPtr m_array_ptr; + const ArrayPayload* m_leaf_ptr = nullptr; + + + LinksToNode(const LinksToNode& source) + : ParentNode(source, nullptr) + , m_target_keys(source.m_target_keys) + , m_column_type(source.m_column_type) + { + } +}; + +} // namespace realm + +#endif // REALM_QUERY_ENGINE_HPP diff --git a/src/vendor-include/realm-ios/include/realm/query_expression.hpp b/src/vendor-include/realm-ios/include/realm/query_expression.hpp new file mode 100644 index 000000000..374dc88ce --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/query_expression.hpp @@ -0,0 +1,4002 @@ +/************************************************************************* + * + * 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 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 +#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 _impl { + +template +inline T only_numeric(U in) +{ + return static_cast(util::unwrap(in)); +} + +template +inline int only_numeric(const StringData&) +{ + REALM_ASSERT(false); + return 0; +} + +template +inline int only_numeric(const BinaryData&) +{ + REALM_ASSERT(false); + return 0; +} + +template +inline StringData only_string_op_types(T in) +{ + REALM_ASSERT(false); + static_cast(in); + return StringData(); +} + +inline BinaryData only_string_op_types(BinaryData in) +{ + return in; +} + +template <> +inline StringData only_string_op_types(StringData in) +{ + return in; +} + +template +inline T no_timestamp(U in) +{ + return static_cast(util::unwrap(in)); +} + +template +inline int no_timestamp(const Timestamp&) +{ + REALM_ASSERT(false); + return 0; +} + +} // namespace _impl + +#endif // REALM_OLDQUERY_FALLBACK + +template +struct Plus { + T operator()(T v1, T v2) const + { + return v1 + v2; + } + static std::string description() + { + return "+"; + } + typedef T type; +}; + +template +struct Minus { + T operator()(T v1, T v2) const + { + return v1 - v2; + } + static std::string description() + { + return "-"; + } + typedef T type; +}; + +template +struct Div { + T operator()(T v1, T v2) const + { + return v1 / v2; + } + static std::string description() + { + return "/"; + } + typedef T type; +}; + +template +struct Mul { + T operator()(T v1, T v2) const + { + return v1 * v2; + } + static std::string description() + { + return "*"; + } + typedef T type; +}; + +// Unary operator +template +struct Pow { + T operator()(T v) const + { + return v * v; + } + static std::string description() + { + return "^"; + } + 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 ValueBase { + static const size_t chunk_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_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 double init() + { + return 50.0; // Default dT + } + + virtual size_t find_first(size_t start, size_t end) const = 0; + virtual void set_base_table(ConstTableRef table) = 0; + virtual void set_cluster(const Cluster*) = 0; + virtual void collect_dependencies(std::vector&) const + { + } + virtual ConstTableRef get_base_table() const = 0; + virtual std::string description(util::serializer::SerialisationState& state) const = 0; + + virtual std::unique_ptr clone(Transaction*) const = 0; +}; + +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(Transaction* = nullptr) const = 0; + + // 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(ConstTableRef) {} + + virtual std::string description(util::serializer::SerialisationState& state) const = 0; + + virtual void set_cluster(const Cluster*) + { + } + + // 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 ConstTableRef get_base_table() const + { + return nullptr; + } + + virtual void collect_dependencies(std::vector&) const + { + } + + virtual bool has_constant_evaluation() const + { + return false; + } + + virtual bool has_search_index() const + { + return false; + } + + virtual std::vector find_all(Mixed) const + { + return {}; + } + + virtual void evaluate(size_t index, ValueBase& destination) = 0; + // This function supports SubColumnAggregate + virtual void evaluate(ObjKey, ValueBase&) + { + REALM_ASSERT(false); // Unimplemented + } +}; + +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()) { + ConstTableRef t = column->get_base_table(); + Query q = Query(t); + + if (std::is_same::value) + q.greater(column->column_key(), _impl::only_numeric(left)); + else if (std::is_same::value) + q.less(column->column_key(), _impl::only_numeric(left)); + else if (std::is_same::value) + q.equal(column->column_key(), left); + else if (std::is_same::value) + q.not_equal(column->column_key(), left); + else if (std::is_same::value) + q.greater_equal(column->column_key(), _impl::only_numeric(left)); + else if (std::is_same::value) + q.less_equal(column->column_key(), _impl::only_numeric(left)); + else if (std::is_same::value) + q.equal(column->column_key(), _impl::only_string_op_types(left), false); + else if (std::is_same::value) + q.not_equal(column->column_key(), _impl::only_string_op_types(left), false); + else if (std::is_same::value) + q.begins_with(column->column_key(), _impl::only_string_op_types(left)); + else if (std::is_same::value) + q.begins_with(column->column_key(), _impl::only_string_op_types(left), false); + else if (std::is_same::value) + q.ends_with(column->column_key(), _impl::only_string_op_types(left)); + else if (std::is_same::value) + q.ends_with(column->column_key(), _impl::only_string_op_types(left), false); + else if (std::is_same::value) + q.contains(column->column_key(), _impl::only_string_op_types(left)); + else if (std::is_same::value) + q.contains(column->column_key(), _impl::only_string_op_types(left), false); + else if (std::is_same::value) + q.like(column->column_key(), _impl::only_string_op_types(left)); + else if (std::is_same::value) + q.like(column->column_key(), _impl::only_string_op_types(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) { + ConstTableRef t = left_col->get_base_table(); + Query q = Query(t); + + if (std::numeric_limits::is_integer) { + if (std::is_same::value) + q.less_int(left_col->column_key(), right_col->column_key()); + else if (std::is_same::value) + q.greater_int(left_col->column_key(), right_col->column_key()); + else if (std::is_same::value) + q.equal_int(left_col->column_key(), right_col->column_key()); + else if (std::is_same::value) + q.not_equal_int(left_col->column_key(), right_col->column_key()); + else if (std::is_same::value) + q.less_equal_int(left_col->column_key(), right_col->column_key()); + else if (std::is_same::value) + q.greater_equal_int(left_col->column_key(), right_col->column_key()); + else { + REALM_ASSERT(false); + } + } + else if (std::is_same::value) { + if (std::is_same::value) + q.less_float(left_col->column_key(), right_col->column_key()); + else if (std::is_same::value) + q.greater_float(left_col->column_key(), right_col->column_key()); + else if (std::is_same::value) + q.equal_float(left_col->column_key(), right_col->column_key()); + else if (std::is_same::value) + q.not_equal_float(left_col->column_key(), right_col->column_key()); + else if (std::is_same::value) + q.less_equal_float(left_col->column_key(), right_col->column_key()); + else if (std::is_same::value) + q.greater_equal_float(left_col->column_key(), right_col->column_key()); + else { + REALM_ASSERT(false); + } + } + else if (std::is_same::value) { + if (std::is_same::value) + q.less_double(left_col->column_key(), right_col->column_key()); + else if (std::is_same::value) + q.greater_double(left_col->column_key(), right_col->column_key()); + else if (std::is_same::value) + q.equal_double(left_col->column_key(), right_col->column_key()); + else if (std::is_same::value) + q.not_equal_double(left_col->column_key(), right_col->column_key()); + else if (std::is_same::value) + q.less_equal_double(left_col->column_key(), right_col->column_key()); + else if (std::is_same::value) + q.greater_equal_double(left_col->column_key(), right_col->column_key()); + 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: + 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(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 { +}; + +template <> +class Subexpr2 : public Subexpr, public Overloads { +public: + Query equal(StringData sd, bool case_sensitive = true); + Query equal(const Subexpr2& col, bool case_sensitive = true); + Query not_equal(StringData sd, bool case_sensitive = true); + Query not_equal(const Subexpr2& col, bool case_sensitive = true); + Query begins_with(StringData sd, bool case_sensitive = true); + Query begins_with(const Subexpr2& col, bool case_sensitive = true); + Query ends_with(StringData sd, bool case_sensitive = true); + Query ends_with(const Subexpr2& col, bool case_sensitive = true); + Query contains(StringData sd, bool case_sensitive = true); + Query contains(const Subexpr2& col, bool case_sensitive = true); + Query like(StringData sd, bool case_sensitive = true); + Query like(const Subexpr2& col, bool case_sensitive = true); +}; + +template <> +class Subexpr2 : public Subexpr, public Overloads { +public: + Query equal(BinaryData sd, bool case_sensitive = true); + Query equal(const Subexpr2& col, bool case_sensitive = true); + Query not_equal(BinaryData sd, bool case_sensitive = true); + Query not_equal(const Subexpr2& col, bool case_sensitive = true); + Query begins_with(BinaryData sd, bool case_sensitive = true); + Query begins_with(const Subexpr2& col, bool case_sensitive = true); + Query ends_with(BinaryData sd, bool case_sensitive = true); + Query ends_with(const Subexpr2& col, bool case_sensitive = true); + Query contains(BinaryData sd, bool case_sensitive = true); + Query contains(const Subexpr2& col, bool case_sensitive = true); + Query like(BinaryData sd, bool case_sensitive = true); + Query like(const Subexpr2& col, bool case_sensitive = true); +}; + + +/* +This class is used to store N values of type T = {int64_t, bool 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 init(const std::vector& values) + { + size_t sz = values.size(); + init(sz); + for (size_t t = 0; t < sz; t++) { + set(t, values[t]); + } + } + + 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; +} + +// 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(); +} + +// 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{}; +} + +// ref_type +template <> +inline bool NullableVector::is_null(size_t index) const +{ + return m_first[index] == 0; +} +template <> +inline void NullableVector::set_null(size_t index) +{ + m_first[index] = 0; +} + +// SizeOfList +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].set_null(); +} + +// Key +template <> +inline bool NullableVector::is_null(size_t index) const +{ + return m_first[index] == null_key; +} +template <> +inline void NullableVector::set_null(size_t index) +{ + m_first[index] = ObjKey{}; +} + +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); + } +}; + + +struct TrueExpression : Expression { + size_t find_first(size_t start, size_t end) const override + { + REALM_ASSERT(start <= end); + if (start != end) + return start; + + return realm::not_found; + } + void set_base_table(ConstTableRef) override {} + void set_cluster(const Cluster*) override + { + } + ConstTableRef get_base_table() const override + { + return nullptr; + } + std::string description(util::serializer::SerialisationState&) const override + { + return "TRUEPREDICATE"; + } + std::unique_ptr clone(Transaction*) const override + { + return std::unique_ptr(new TrueExpression(*this)); + } +}; + + +struct FalseExpression : Expression { + size_t find_first(size_t, size_t) const override + { + return realm::not_found; + } + void set_base_table(ConstTableRef) override {} + void set_cluster(const Cluster*) override + { + } + std::string description(util::serializer::SerialisationState&) const override + { + return "FALSEPREDICATE"; + } + ConstTableRef get_base_table() const override + { + return nullptr; + } + std::unique_ptr clone(Transaction*) const override + { + return std::unique_ptr(new FalseExpression(*this)); + } +}; + + +// 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, 1, T()); + } + Value(T v) + { + init(false, 1, 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 init(bool from_link_list, const std::vector& values) + { + m_storage.init(values); + ValueBase::m_from_link_list = from_link_list; + ValueBase::m_values = values.size(); + } + + std::string description(util::serializer::SerialisationState&) const override + { + if (ValueBase::m_from_link_list) { + return util::serializer::print_value(util::to_string(ValueBase::m_values) + + (ValueBase::m_values == 1 ? " value" : " values")); + } + if (m_storage.m_size > 0) { + return util::serializer::print_value(m_storage[0]); + } + return ""; + } + + bool has_constant_evaluation() const override + { + return true; + } + + 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_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) + 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_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_const(const Value* left, Value* right) + { + TCond c; + + size_t sz = right->ValueBase::m_values; + bool left_is_null = left->m_storage.is_null(0); + for (size_t m = 0; m < sz; m++) { + if (c(left->m_storage[0], right->m_storage[m], left_is_null, right->m_storage.is_null(m))) + return right->m_from_link_list ? 0 : m; + } + + return not_found; // no 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(Transaction*) 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, 1, m_string); + } + + std::unique_ptr clone(Transaction*) 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==(bool 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!=(bool 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 key within 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 object (it will be a link tree if you have multiple type_LinkList columns + // in a link()->link() query. + virtual bool consume(ObjKey) = 0; +}; + +struct FindNullLinks : public LinkMapFunction { + bool consume(ObjKey) override + { + m_has_link = true; + return false; // we've found a key, 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(ObjKey key) override + { + m_links.push_back(key); + return true; // continue evaluation + } + std::vector& m_links; +}; + +struct UnaryLinkResult : public LinkMapFunction { + bool consume(ObjKey key) override + { + m_result = key; + return false; // exit search, only one result ever expected + } + ObjKey m_result; +}; + +struct CountLinks : public LinkMapFunction { + bool consume(ObjKey) override + { + m_link_count++; + return true; + } + + size_t result() const + { + return m_link_count; + } + + size_t m_link_count = 0; +}; + +struct CountBacklinks : public LinkMapFunction { + CountBacklinks(ConstTableRef t) + : m_table(t) + { + } + + bool consume(ObjKey key) override + { + m_link_count += m_table.unchecked_ptr()->get_object(key).get_backlink_count(); + return true; + } + + size_t result() const + { + return m_link_count; + } + + ConstTableRef m_table; + 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 array 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 key(s). + +If all link columns are type_Link, then LinkMapFunction is only invoked for a single key. If one or more +columns are type_LinkList, then it may result in multiple keys. + +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 key '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(ConstTableRef table, std::vector columns) + : m_link_column_keys(std::move(columns)) + { + set_base_table(table); + } + + LinkMap(LinkMap const& other) + { + m_link_column_keys = other.m_link_column_keys; + m_tables = other.m_tables; + m_link_types = other.m_link_types; + m_only_unary_links = other.m_only_unary_links; + } + + size_t get_nb_hops() const + { + return m_link_column_keys.size(); + } + + bool has_links() const + { + return m_link_column_keys.size() > 0; + } + + void set_base_table(ConstTableRef table); + + void set_cluster(const Cluster* cluster) + { + Allocator& alloc = m_tables.back()->get_alloc(); + m_array_ptr = nullptr; + switch (m_link_types[0]) { + case col_type_Link: + m_array_ptr = LeafPtr(new (&m_storage.m_list) ArrayKey(alloc)); + break; + case col_type_LinkList: + m_array_ptr = LeafPtr(new (&m_storage.m_linklist) ArrayList(alloc)); + break; + case col_type_BackLink: + m_array_ptr = LeafPtr(new (&m_storage.m_backlink) ArrayBacklink(alloc)); + break; + default: + break; + } + // m_tables[0]->report_invalid_key(m_link_column_keys[0]); + cluster->init_leaf(m_link_column_keys[0], m_array_ptr.get()); + m_leaf_ptr = m_array_ptr.get(); + } + + void collect_dependencies(std::vector& tables) const; + + virtual std::string description(util::serializer::SerialisationState& state) const; + + ObjKey get_unary_link_or_not_found(size_t index) const + { + REALM_ASSERT(m_only_unary_links); + UnaryLinkResult res; + map_links(index, res); + return res.m_result; + } + + std::vector get_links(size_t index) const + { + std::vector res; + get_links(index, res); + return res; + } + + std::vector get_origin_ndxs(ObjKey key, size_t column = 0) const; + + size_t count_links(size_t row) const + { + CountLinks counter; + map_links(row, counter); + return counter.result(); + } + + size_t count_all_backlinks(size_t row) const + { + CountBacklinks counter(get_target_table()); + map_links(row, counter); + return counter.result(); + } + + void map_links(size_t row, LinkMapFunction& lm) const + { + map_links(0, row, lm); + } + + bool only_unary_links() const + { + return m_only_unary_links; + } + + ConstTableRef get_base_table() const + { + return m_tables.empty() ? nullptr : m_tables[0]; + } + + ConstTableRef get_target_table() const + { + REALM_ASSERT(!m_tables.empty()); + return m_tables.back(); + } + + bool links_exist() const + { + return !m_link_column_keys.empty(); + } + +private: + void map_links(size_t column, ObjKey key, LinkMapFunction& lm) const; + void map_links(size_t column, size_t row, LinkMapFunction& lm) const; + + void get_links(size_t row, std::vector& result) const + { + MakeLinkVector mlv = MakeLinkVector(result); + map_links(row, mlv); + } + + mutable std::vector m_link_column_keys; + std::vector m_link_types; + std::vector m_tables; + bool m_only_unary_links = true; + // Leaf cache + using LeafPtr = std::unique_ptr; + union Storage { + typename std::aligned_storage::type m_list; + typename std::aligned_storage::type m_linklist; + typename std::aligned_storage::type m_backlink; + }; + Storage m_storage; + LeafPtr m_array_ptr; + const ArrayPayload* m_leaf_ptr = nullptr; + + template + friend Query compare(const Subexpr2&, const ConstObj&); +}; + +template +Query string_compare(const Subexpr2& left, T right, bool case_insensitive); +template +Query string_compare(const Subexpr2& left, const Subexpr2& 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(ColKey column, ConstTableRef table, std::vector links = {}) + : m_link_map(table, std::move(links)) + , m_column_key(column) + { + } + + bool is_nullable() const noexcept + { + return m_link_map.get_base_table()->is_nullable(m_column_key); + } + + ConstTableRef get_base_table() const override + { + return m_link_map.get_base_table(); + } + + void set_base_table(ConstTableRef table) override + { + if (table != get_base_table()) { + m_link_map.set_base_table(table); + } + } + + void set_cluster(const Cluster* cluster) override + { + m_array_ptr = nullptr; + m_leaf_ptr = nullptr; + if (links_exist()) { + m_link_map.set_cluster(cluster); + } + else { + // Create new Leaf + m_array_ptr = LeafPtr(new (&m_leaf_cache_storage) LeafType(m_link_map.get_base_table()->get_alloc())); + cluster->init_leaf(m_column_key, m_array_ptr.get()); + m_leaf_ptr = m_array_ptr.get(); + } + } + + bool has_search_index() const override + { + return m_link_map.get_target_table()->has_search_index(m_column_key); + } + + std::vector find_all(Mixed value) const override + { + std::vector ret; + std::vector result; + + T val{}; + if (!value.is_null()) { + val = value.get(); + } + + StringIndex* index = m_link_map.get_target_table()->get_search_index(m_column_key); + index->find_all(result, val); + + for (ObjKey k : result) { + auto ndxs = m_link_map.get_origin_ndxs(k); + ret.insert(ret.end(), ndxs.begin(), ndxs.end()); + } + + return ret; + } + + void collect_dependencies(std::vector& tables) const override + { + m_link_map.collect_dependencies(tables); + } + + void evaluate(size_t index, ValueBase& destination) override + { + Value& d = static_cast&>(destination); + + if (links_exist()) { + REALM_ASSERT(m_leaf_ptr == nullptr); + + if (m_link_map.only_unary_links()) { + d.init(false, 1); + d.m_storage.set_null(0); + auto link_translation_key = this->m_link_map.get_unary_link_or_not_found(index); + if (link_translation_key) { + ConstObj obj = m_link_map.get_target_table()->get_object(link_translation_key); + d.m_storage.set(0, obj.get(m_column_key)); + } + } + else { + std::vector links = m_link_map.get_links(index); + Value v = make_value_for_link(false /*only_unary_links*/, links.size()); + for (size_t t = 0; t < links.size(); t++) { + ConstObj obj = m_link_map.get_target_table()->get_object(links[t]); + v.m_storage.set(t, obj.get(m_column_key)); + } + destination.import(v); + } + } + else { + REALM_ASSERT(m_leaf_ptr != nullptr); + // Not a link column + for (size_t t = 0; t < destination.m_values && index + t < m_leaf_ptr->size(); t++) { + d.m_storage.set(t, m_leaf_ptr->get(index + t)); + } + } + } + + void evaluate(ObjKey key, ValueBase& destination) override + { + Value& d = static_cast&>(destination); + d.m_storage.set(0, m_link_map.get_target_table()->get_object(key).template get(m_column_key)); + } + + bool links_exist() const + { + return m_link_map.has_links(); + } + + bool only_unary_links() const + { + return m_link_map.only_unary_links(); + } + + LinkMap get_link_map() const + { + return m_link_map; + } + + virtual std::string description(util::serializer::SerialisationState& state) const override + { + return state.describe_columns(m_link_map, m_column_key); + } + + std::unique_ptr clone(Transaction* = nullptr) const override + { + return make_subexpr>(static_cast&>(*this)); + } + + SimpleQuerySupport(SimpleQuerySupport const& other) + : Subexpr2(other) + , m_link_map(other.m_link_map) + , m_column_key(other.m_column_key) + { + } + + ColKey column_key() const + { + return m_column_key; + } + + SizeOperator size() + { + return SizeOperator(this->clone(nullptr)); + } + +private: + LinkMap m_link_map; + + // Column key of payload column of m_table + mutable ColKey m_column_key; + + // Leaf cache + using LeafType = typename ColumnTypeTraits::cluster_leaf_type; + using LeafCacheStorage = typename std::aligned_storage::type; + using LeafPtr = std::unique_ptr; + LeafCacheStorage m_leaf_cache_storage; + LeafPtr m_array_ptr; + LeafType* m_leaf_ptr = nullptr; +}; + + +template <> +class Columns : public SimpleQuerySupport { + using SimpleQuerySupport::SimpleQuerySupport; +}; + +template <> +class Columns : public SimpleQuerySupport { + using SimpleQuerySupport::SimpleQuerySupport; +}; + +template <> +class Columns : public SimpleQuerySupport { +public: + Columns(ColKey column, ConstTableRef table, std::vector links = {}) + : SimpleQuerySupport(column, table, links) + { + } + + Columns(Columns const& other) + : SimpleQuerySupport(other) + { + } + + Columns(Columns&& other) noexcept + : SimpleQuerySupport(other) + { + } + using SimpleQuerySupport::size; +}; + +template +Query string_compare(const Subexpr2& 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 Subexpr2& left, const Subexpr2& right, bool case_sensitive) +{ + if (case_sensitive) + return make_expression>(right.clone(), left.clone()); + else + return make_expression>(right.clone(), left.clone()); +} + +template +Query binary_compare(const Subexpr2& left, T right, bool case_sensitive) +{ + BinaryData data(right); + if (case_sensitive) + return create(data, left); + else + return create(data, left); +} + +template +Query binary_compare(const Subexpr2& left, const Subexpr2& 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(const LinkMap& lm) + : m_link_map(lm) + { + } + + void set_base_table(ConstTableRef table) override + { + m_link_map.set_base_table(table); + } + + void set_cluster(const Cluster* cluster) override + { + m_link_map.set_cluster(cluster); + } + + void collect_dependencies(std::vector& tables) const override + { + m_link_map.collect_dependencies(tables); + } + + // 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 + ConstTableRef get_base_table() const override + { + return m_link_map.get_base_table(); + } + + size_t find_first(size_t start, size_t end) const override + { + for (; start < end;) { + FindNullLinks fnl; + m_link_map.map_links(start, fnl); + if (fnl.m_has_link == has_links) + return start; + + start++; + } + + return not_found; + } + + virtual std::string description(util::serializer::SerialisationState& state) const override + { + return state.describe_columns(m_link_map, ColKey()) + (has_links ? " != NULL" : " == NULL"); + } + + std::unique_ptr clone(Transaction*) const override + { + return std::unique_ptr(new UnaryLinkCompare(*this)); + } + +private: + UnaryLinkCompare(const UnaryLinkCompare& other) + : Expression(other) + , m_link_map(other.m_link_map) + { + } + + mutable LinkMap m_link_map; +}; + +class LinkCount : public Subexpr2 { +public: + LinkCount(const LinkMap& link_map) + : m_link_map(link_map) + { + } + LinkCount(LinkCount const& other) + : Subexpr2(other) + , m_link_map(other.m_link_map) + { + } + + std::unique_ptr clone(Transaction*) const override + { + return make_subexpr(*this); + } + + ConstTableRef get_base_table() const override + { + return m_link_map.get_base_table(); + } + + void set_base_table(ConstTableRef table) override + { + m_link_map.set_base_table(table); + } + + void set_cluster(const Cluster* cluster) override + { + m_link_map.set_cluster(cluster); + } + + void collect_dependencies(std::vector& tables) const override + { + m_link_map.collect_dependencies(tables); + } + + void evaluate(size_t index, ValueBase& destination) override + { + size_t count = m_link_map.count_links(index); + destination.import(Value(false, 1, count)); + } + + virtual std::string description(util::serializer::SerialisationState& state) const override + { + return state.describe_columns(m_link_map, ColKey()) + util::serializer::value_separator + "@count"; + } + +private: + LinkMap m_link_map; +}; + +// Gives a count of all backlinks across all columns for the specified row. +// The unused template parameter is a hack to avoid a circular dependency between table.hpp and query_expression.hpp. +template +class BacklinkCount : public Subexpr2 { +public: + BacklinkCount(const LinkMap& link_map) + : m_link_map(link_map) + { + } + BacklinkCount(LinkMap&& link_map) + : m_link_map(std::move(link_map)) + { + } + BacklinkCount(ConstTableRef table, std::vector links = {}) + : m_link_map(table, std::move(links)) + { + } + BacklinkCount(BacklinkCount const& other) + : Subexpr2(other) + , m_link_map(other.m_link_map) + { + } + + std::unique_ptr clone(Transaction*) const override + { + return make_subexpr>(*this); + } + + ConstTableRef get_base_table() const override + { + return m_link_map.get_base_table(); + } + + void set_base_table(ConstTableRef table) override + { + m_link_map.set_base_table(table); + } + + void set_cluster(const Cluster* cluster) override + { + if (m_link_map.has_links()) { + m_link_map.set_cluster(cluster); + } + else { + m_keys = cluster->get_key_array(); + m_offset = cluster->get_offset(); + } + } + + void collect_dependencies(std::vector& tables) const override + { + m_link_map.collect_dependencies(tables); + } + + void evaluate(size_t index, ValueBase& destination) override + { + size_t count; + if (m_link_map.has_links()) { + count = m_link_map.count_all_backlinks(index); + } + else { + ObjKey key(m_keys->get(index) + m_offset); + ConstObj obj = m_link_map.get_base_table()->get_object(key); + count = obj.get_backlink_count(); + } + destination.import(Value(false, 1, count)); + } + + virtual std::string description(util::serializer::SerialisationState& state) const override + { + std::string s; + if (m_link_map.links_exist()) { + s += state.describe_columns(m_link_map, ColKey()) + util::serializer::value_separator; + } + s += "@links.@count"; + return s; + } + +private: + const ClusterKeyArray* m_keys = nullptr; + uint64_t m_offset = 0; + 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(ConstTableRef table) override + { + m_expr->set_base_table(table); + } + + void set_cluster(const Cluster* cluster) override + { + m_expr->set_cluster(cluster); + } + + // 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 + ConstTableRef 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, elem->size()); + } + } + } + + std::string description(util::serializer::SerialisationState& state) const override + { + if (m_expr) { + return m_expr->description(state) + util::serializer::value_separator + "@size"; + } + return "@size"; + } + + std::unique_ptr clone(Transaction* tr) const override + { + return std::unique_ptr(new SizeOperator(*this, tr)); + } + +private: + SizeOperator(const SizeOperator& other, Transaction* tr) + : m_expr(other.m_expr->clone(tr)) + { + } + + std::unique_ptr m_expr; +}; + +class KeyValue : public Subexpr2 { +public: + KeyValue(ObjKey key) + : m_key(key) + { + } + + void set_base_table(ConstTableRef) override {} + + ConstTableRef get_base_table() const override + { + return nullptr; + } + + void evaluate(size_t, ValueBase& destination) override + { + // Destination must be of Key type. It only makes sense to + // compare keys with keys + REALM_ASSERT_DEBUG(dynamic_cast*>(&destination)); + auto d = static_cast*>(&destination); + d->init(false, 1, m_key); + } + + virtual std::string description(util::serializer::SerialisationState&) const override + { + return util::serializer::print_value(m_key); + } + + std::unique_ptr clone(Transaction*) const override + { + return std::unique_ptr(new KeyValue(*this)); + } + +private: + KeyValue(const KeyValue& source) + : m_key(source.m_key) + { + } + + ObjKey m_key; +}; + +template +class SubColumns; + +// This is for LinkList and BackLink too since they're declared as typedefs of Link. +template <> +class Columns : public Subexpr2 { +public: + Columns(const Columns& other) + : Subexpr2(other) + , m_link_map(other.m_link_map) + { + } + + Query is_null() + { + if (m_link_map.get_nb_hops() > 1) + throw util::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.get_nb_hops() > 1) + throw util::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); + } + + template + BacklinkCount backlink_count() const + { + return BacklinkCount(m_link_map); + } + + template + SubColumns column(ColKey column_key) const + { + return SubColumns(Columns(column_key, m_link_map.get_target_table()), m_link_map); + } + + const LinkMap& link_map() const + { + return m_link_map; + } + + ConstTableRef get_base_table() const override + { + return m_link_map.get_base_table(); + } + + void set_base_table(ConstTableRef table) override + { + m_link_map.set_base_table(table); + } + + void set_cluster(const Cluster* cluster) override + { + REALM_ASSERT(m_link_map.has_links()); + m_link_map.set_cluster(cluster); + } + + void collect_dependencies(std::vector& tables) const override + { + m_link_map.collect_dependencies(tables); + } + + std::string description(util::serializer::SerialisationState& state) const override + { + return state.describe_columns(m_link_map, ColKey()); + } + + std::unique_ptr clone(Transaction*) const override + { + return std::unique_ptr(new Columns(*this)); + } + + void evaluate(size_t index, ValueBase& destination) override; + + +private: + LinkMap m_link_map; + friend class Table; + friend class LinkChain; + + Columns(ColKey column_key, ConstTableRef table, const std::vector& links = {}) + : m_link_map(table, links) + { + static_cast(column_key); + } +}; + +template +class ListColumns; +template +class ListColumnAggregate; +namespace aggregate_operations { +template +class Minimum; +template +class Maximum; +template +class Sum; +template +class Average; +} + +class ColumnListBase { +public: + ColumnListBase(ColKey column_key, ConstTableRef table, const std::vector& links) + : m_column_key(column_key) + , m_link_map(table, links) + { + } + + ColumnListBase(const ColumnListBase& other) + : m_column_key(other.m_column_key) + , m_link_map(other.m_link_map) + { + } + + void set_cluster(const Cluster* cluster); + + void get_lists(size_t index, Value& destination, size_t nb_elements); + + std::string description(util::serializer::SerialisationState&) const + { + throw SerialisationError("Serialisation of query expressions involving subtables is not yet supported."); + } + + bool links_exist() const + { + return m_link_map.has_links(); + } + + mutable ColKey m_column_key; + LinkMap m_link_map; + // Leaf cache + using LeafCacheStorage = typename std::aligned_storage::type; + using LeafPtr = std::unique_ptr; + LeafCacheStorage m_leaf_cache_storage; + LeafPtr m_array_ptr; + ArrayList* m_leaf_ptr = nullptr; +}; + +template +class ColumnListSize; + +template +class Columns> : public Subexpr2, public ColumnListBase { +public: + Columns(const Columns>& other) + : Subexpr2(other) + , ColumnListBase(other) + { + } + + std::unique_ptr clone(Transaction*) const override + { + return make_subexpr>>(*this); + } + + ConstTableRef get_base_table() const override + { + return m_link_map.get_base_table(); + } + + void set_base_table(ConstTableRef table) override + { + m_link_map.set_base_table(table); + } + + void set_cluster(const Cluster* cluster) override + { + ColumnListBase::set_cluster(cluster); + } + + void collect_dependencies(std::vector& tables) const override + { + m_link_map.collect_dependencies(tables); + } + + void evaluate(size_t index, ValueBase& destination) override + { + Allocator& alloc = get_base_table()->get_alloc(); + Value list_refs; + get_lists(index, list_refs, 1); + size_t sz = 0; + for (size_t i = 0; i < list_refs.m_values; i++) { + ref_type val = list_refs.m_storage[i]; + if (val) { + char* header = alloc.translate(val); + sz += Array::get_size_from_header(header); + } + } + auto v = make_value_for_link::type>(false, sz); + size_t k = 0; + for (size_t i = 0; i < list_refs.m_values; i++) { + ref_type list_ref = list_refs.m_storage[i]; + if (list_ref) { + BPlusTree list(alloc); + list.init_from_ref(list_ref); + size_t s = list.size(); + for (size_t j = 0; j < s; j++) { + v.m_storage.set(k++, list.get(j)); + } + } + } + destination.import(v); + } + + virtual std::string description(util::serializer::SerialisationState&) const override + { + throw SerialisationError("Serialisation of subtable expressions is not yet supported."); + } + + SizeOperator size(); + + ListColumnAggregate> min() const + { + return {m_column_key, *this}; + } + + ListColumnAggregate> max() const + { + return {m_column_key, *this}; + } + + ListColumnAggregate> sum() const + { + return {m_column_key, *this}; + } + + ListColumnAggregate> average() const + { + return {m_column_key, *this}; + } + + +private: + friend class Table; + friend class LinkChain; + + Columns(ColKey column_key, ConstTableRef table, const std::vector& links = {}) + : ColumnListBase(column_key, table, links) + { + } +}; + +template +class ColumnListSize : public Columns> { +public: + ColumnListSize(const Columns>& other) + : Columns>(other) + { + } + void evaluate(size_t index, ValueBase& destination) override + { + REALM_ASSERT_DEBUG(dynamic_cast*>(&destination) != nullptr); + Value* d = static_cast*>(&destination); + + Allocator& alloc = this->get_base_table()->get_alloc(); + Value list_refs; + this->get_lists(index, list_refs, 1); + d->init(list_refs.m_from_link_list, list_refs.m_values); + + for (size_t i = 0; i < list_refs.m_values; i++) { + ref_type list_ref = list_refs.m_storage[i]; + if (list_ref) { + BPlusTree list(alloc); + list.init_from_ref(list_ref); + size_t s = list.size(); + d->m_storage.set(i, SizeOfList(s)); + } + else { + d->m_storage.set_null(i); + } + } + } + std::unique_ptr clone(Transaction*) const override + { + return std::unique_ptr(new ColumnListSize(*this)); + } +}; + +template +SizeOperator Columns>::size() +{ + std::unique_ptr ptr(new ColumnListSize(*this)); + return SizeOperator(std::move(ptr)); +} + +template +class ListColumnAggregate : public Subexpr2 { +public: + using R = typename Operation::ResultType; + + ListColumnAggregate(ColKey column_key, Columns> column) + : m_column_key(column_key) + , m_list(std::move(column)) + { + } + + ListColumnAggregate(const ListColumnAggregate& other) + : m_column_key(other.m_column_key) + , m_list(other.m_list) + { + } + + std::unique_ptr clone(Transaction*) const override + { + return make_subexpr(*this); + } + + ConstTableRef get_base_table() const override + { + return m_list.get_base_table(); + } + + void set_base_table(ConstTableRef table) override + { + m_list.set_base_table(table); + } + + void set_cluster(const Cluster* cluster) override + { + m_list.set_cluster(cluster); + } + + void collect_dependencies(std::vector& tables) const override + { + m_list.collect_dependencies(tables); + } + + void evaluate(size_t index, ValueBase& destination) override + { + Allocator& alloc = get_base_table()->get_alloc(); + Value list_refs; + m_list.get_lists(index, list_refs, 1); + REALM_ASSERT_DEBUG(list_refs.m_values > 0 || list_refs.m_from_link_list); + size_t sz = list_refs.m_values; + // The result is an aggregate value for each table + auto v = make_value_for_link(!list_refs.m_from_link_list, sz); + for (unsigned i = 0; i < sz; i++) { + auto list_ref = list_refs.m_storage[i]; + Operation op; + if (list_ref) { + BPlusTree list(alloc); + list.init_from_ref(list_ref); + size_t s = list.size(); + for (unsigned j = 0; j < s; j++) { + op.accumulate(list.get(j)); + } + } + if (op.is_null()) { + v.m_storage.set_null(i); + } + else { + v.m_storage.set(i, op.result()); + } + } + destination.import(v); + } + + virtual std::string description(util::serializer::SerialisationState&) const override + { + throw SerialisationError("Serialisation of queries involving subtable expressions is not yet supported."); + } + +private: + ColKey m_column_key; + Columns> m_list; +}; + +template +Query compare(const Subexpr2& left, const ConstObj& obj) +{ + 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.get_target_table()->get_key() == obj.get_table()->get_key()); +#ifdef REALM_OLDQUERY_FALLBACK + if (link_map.get_nb_hops() == 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)) { + ConstTableRef 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_keys[0], obj.get_key()); + return query; + } + } +#endif + } + return make_expression>(left.clone(), make_subexpr(obj.get_key())); +} + +inline Query operator==(const Subexpr2& left, const ConstObj& row) +{ + return compare(left, row); +} +inline Query operator!=(const Subexpr2& left, const ConstObj& row) +{ + return compare(left, row); +} +inline Query operator==(const ConstObj& row, const Subexpr2& right) +{ + return compare(right, row); +} +inline Query operator!=(const ConstObj& 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(ObjKey{})); +} + +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 LeafType = typename ColumnTypeTraits::cluster_leaf_type; + + Columns(ColKey column, ConstTableRef table, std::vector links = {}) + : m_link_map(table, std::move(links)) + , m_column_key(column) + , m_nullable(m_link_map.get_target_table()->is_nullable(m_column_key)) + { + } + + Columns(const Columns& other) + : m_link_map(other.m_link_map) + , m_column_key(other.m_column_key) + , m_nullable(other.m_nullable) + { + } + + Columns& operator=(const Columns& other) + { + if (this != &other) { + m_link_map = other.m_link_map; + m_column_key = other.m_column_key; + m_nullable = other.m_nullable; + } + return *this; + } + + std::unique_ptr clone(Transaction*) const override + { + return make_subexpr>(*this); + } + + // See comment in base class + void set_base_table(ConstTableRef table) override + { + if (table == get_base_table()) + return; + + m_link_map.set_base_table(table); + m_nullable = m_link_map.get_target_table()->is_nullable(m_column_key); + } + + void set_cluster(const Cluster* cluster) override + { + m_array_ptr = nullptr; + m_leaf_ptr = nullptr; + if (links_exist()) { + m_link_map.set_cluster(cluster); + } + else { + // Create new Leaf + m_array_ptr = LeafPtr(new (&m_leaf_cache_storage) LeafType(get_base_table()->get_alloc())); + cluster->init_leaf(m_column_key, m_array_ptr.get()); + m_leaf_ptr = m_array_ptr.get(); + } + } + + bool has_search_index() const override + { + return m_link_map.get_target_table()->has_search_index(m_column_key); + } + + std::vector find_all(Mixed value) const override + { + std::vector ret; + std::vector result; + + if (m_nullable && std::is_same::value) { + util::Optional val; + if (!value.is_null()) { + val = value.get_int(); + } + StringIndex* index = m_link_map.get_target_table()->get_search_index(m_column_key); + index->find_all(result, val); + } + else { + T val{}; + if (!value.is_null()) { + val = value.get(); + } + StringIndex* index = m_link_map.get_target_table()->get_search_index(m_column_key); + index->find_all(result, val); + } + + for (auto k : result) { + auto ndxs = m_link_map.get_origin_ndxs(k); + ret.insert(ret.end(), ndxs.begin(), ndxs.end()); + } + + return ret; + } + + void collect_dependencies(std::vector& tables) const override + { + m_link_map.collect_dependencies(tables); + } + + // 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 + ConstTableRef get_base_table() const override + { + return m_link_map.get_base_table(); + } + + template + void evaluate_internal(size_t index, ValueBase& destination) + { + using U = typename LeafType2::value_type; + + if (links_exist()) { + REALM_ASSERT(m_leaf_ptr == nullptr); + // 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++) { + ConstObj obj = m_link_map.get_target_table()->get_object(links[t]); + if (obj.is_null(m_column_key)) + v.m_storage.set_null(t); + else + v.m_storage.set(t, obj.get(m_column_key)); + } + destination.import(v); + } + else { + REALM_ASSERT(m_leaf_ptr != nullptr); + auto leaf = static_cast(m_leaf_ptr); + // Not a Link column + size_t colsize = leaf->size(); + + // Now load `ValueBase::chunk_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::chunk_size <= colsize) { + Value v(false, ValueBase::chunk_size); + + // If you want to modify 'chunk_size' then update Array::get_chunk() + REALM_ASSERT_3(ValueBase::chunk_size, ==, 8); + + auto leaf_2 = static_cast(leaf); + leaf_2->get_chunk(index, v.m_storage.m_first); + + destination.import(v); + } + else { + size_t rows = colsize - index; + if (rows > ValueBase::chunk_size) + rows = ValueBase::chunk_size; + Value::type> v(false, rows); + + for (size_t t = 0; t < rows; t++) + v.m_storage.set(t, leaf->get(index + t)); + + destination.import(v); + } + } + } + + virtual std::string description(util::serializer::SerialisationState& state) const override + { + return state.describe_columns(m_link_map, m_column_key); + } + + // 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 if (m_nullable && std::is_same::value) { + evaluate_internal(index, destination); + } + else { + evaluate_internal(index, destination); + } + } + + void evaluate(ObjKey key, ValueBase& destination) override + { + auto table = m_link_map.get_target_table(); + auto obj = table.unchecked_ptr()->get_object(key); + if (m_nullable && std::is_same::value) { + Value v(false, 1); + v.m_storage.set(0, obj.template get>(m_column_key)); + destination.import(v); + } + else if (m_nullable && std::is_same::value) { + Value v(false, 1); + v.m_storage.set(0, obj.template get>(m_column_key)); + destination.import(v); + } + else { + Value::type> v(false, 1); + T val = obj.template get(m_column_key); + v.m_storage.set(0, val); + destination.import(v); + } + } + + bool links_exist() const + { + return m_link_map.has_links(); + } + + bool only_unary_links() const + { + return m_link_map.only_unary_links(); + } + + bool is_nullable() const + { + return m_nullable; + } + + LinkMap get_link_map() const + { + return m_link_map; + } + + ColKey column_key() const noexcept + { + return m_column_key; + } + +private: + LinkMap m_link_map; + + // Leaf cache + using LeafCacheStorage = typename std::aligned_storage::type; + using LeafPtr = std::unique_ptr; + LeafCacheStorage m_leaf_cache_storage; + LeafPtr m_array_ptr; + const ArrayPayload* m_leaf_ptr = nullptr; + + // Column index of payload column of m_table + mutable ColKey m_column_key; + + // 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; +}; + +template +class SubColumnAggregate; + +template +class SubColumns : public Subexpr { +public: + SubColumns(Columns&& column, const LinkMap& link_map) + : m_column(std::move(column)) + , m_link_map(link_map) + { + } + + std::unique_ptr clone(Transaction*) const override + { + return make_subexpr>(*this); + } + + ConstTableRef get_base_table() const override + { + return m_link_map.get_base_table(); + } + + void set_base_table(ConstTableRef table) override + { + m_link_map.set_base_table(table); + m_column.set_base_table(m_link_map.get_target_table()); + } + + void collect_dependencies(std::vector& tables) const override + { + m_link_map.collect_dependencies(tables); + } + + void evaluate(size_t, ValueBase&) override + { + // SubColumns can only be used in an expression in conjunction with its aggregate methods. + REALM_ASSERT(false); + } + + virtual std::string description(util::serializer::SerialisationState&) const override + { + return ""; // by itself there are no conditions, see SubColumnAggregate + } + + 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(const Columns& column, const LinkMap& link_map) + : m_column(column) + , m_link_map(link_map) + { + } + SubColumnAggregate(SubColumnAggregate const& other) + : m_column(other.m_column) + , m_link_map(other.m_link_map) + { + } + + std::unique_ptr clone(Transaction*) const override + { + return make_subexpr(*this); + } + + ConstTableRef get_base_table() const override + { + return m_link_map.get_base_table(); + } + + void set_base_table(ConstTableRef table) override + { + m_link_map.set_base_table(table); + m_column.set_base_table(m_link_map.get_target_table()); + } + + void set_cluster(const Cluster* cluster) override + { + m_link_map.set_cluster(cluster); + } + + void collect_dependencies(std::vector& tables) const override + { + m_link_map.collect_dependencies(tables); + } + + void evaluate(size_t index, ValueBase& destination) override + { + std::vector keys = m_link_map.get_links(index); + std::sort(keys.begin(), keys.end()); + + Operation op; + for (auto key : keys) { + Value value(false, 1); + m_column.evaluate(key, value); + size_t value_index = 0; + const auto& value_storage = value.m_storage; + if (!value_storage.is_null(value_index)) { + op.accumulate(value_storage[value_index]); + } + } + if (op.is_null()) { + destination.import(Value(false, 1, null())); + } + else { + destination.import(Value(false, 1, op.result())); + } + } + + virtual std::string description(util::serializer::SerialisationState& state) const override + { + util::serializer::SerialisationState empty_state; + return state.describe_columns(m_link_map, ColKey()) + util::serializer::value_separator + + Operation::description() + util::serializer::value_separator + m_column.description(empty_state); + } + +private: + Columns m_column; + LinkMap m_link_map; +}; + +class SubQueryCount : public Subexpr2 { +public: + SubQueryCount(const Query& q, const LinkMap& link_map) + : m_query(q) + , m_link_map(link_map) + { + } + + ConstTableRef get_base_table() const override + { + return m_link_map.get_base_table(); + } + + void set_base_table(ConstTableRef table) override + { + m_link_map.set_base_table(table); + } + + void set_cluster(const Cluster* cluster) override + { + m_link_map.set_cluster(cluster); + } + + void collect_dependencies(std::vector& tables) const override + { + m_link_map.collect_dependencies(tables); + } + + void evaluate(size_t index, ValueBase& destination) override + { + std::vector links = m_link_map.get_links(index); + // std::sort(links.begin(), links.end()); + m_query.init(); + + size_t count = std::accumulate(links.begin(), links.end(), size_t(0), [this](size_t running_count, ObjKey k) { + ConstObj obj = m_link_map.get_target_table()->get_object(k); + return running_count + m_query.eval_object(obj); + }); + + destination.import(Value(false, 1, size_t(count))); + } + + virtual std::string description(util::serializer::SerialisationState& state) const override + { + REALM_ASSERT(m_link_map.get_base_table() != nullptr); + std::string target = state.describe_columns(m_link_map, ColKey()); + std::string var_name = state.get_variable_name(m_link_map.get_base_table()); + state.subquery_prefix_list.push_back(var_name); + std::string desc = "SUBQUERY(" + target + ", " + var_name + ", " + m_query.get_description(state) + ")" + + util::serializer::value_separator + "@count"; + state.subquery_prefix_list.pop_back(); + return desc; + } + + std::unique_ptr clone(Transaction* tr) const override + { + if (tr) + return std::unique_ptr(new SubQueryCount(*this, tr)); + + return make_subexpr(*this); + } + +private: + SubQueryCount(const SubQueryCount& other, Transaction* tr) + : m_link_map(other.m_link_map) + { + m_query = Query(other.m_query, tr, PayloadPolicy::Copy); + } + + 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.get_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); + } + static std::string description() + { + return "@min"; + } +}; + +template +class Maximum : public BaseAggregateOperation> { +public: + static T initial_value() + { + return std::numeric_limits::lowest(); + } + static T apply(T a, T b) + { + return std::max(a, b); + } + static std::string description() + { + return "@max"; + } +}; + +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; + } + static std::string description() + { + return "@sum"; + } +}; + +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; + } + static std::string description() + { + return "@avg"; + } +}; +} + +template +class UnaryOperator : public Subexpr2 { +public: + UnaryOperator(std::unique_ptr left) + : m_left(std::move(left)) + { + } + + UnaryOperator(const UnaryOperator& other, Transaction* tr) + : m_left(other.m_left->clone(tr)) + { + } + + UnaryOperator& operator=(const UnaryOperator& other) + { + if (this != &other) { + m_left = other.m_left->clone(); + } + return *this; + } + + UnaryOperator(UnaryOperator&&) noexcept = default; + UnaryOperator& operator=(UnaryOperator&&) noexcept = default; + + // See comment in base class + void set_base_table(ConstTableRef table) override + { + m_left->set_base_table(table); + } + + void set_cluster(const Cluster* cluster) override + { + m_left->set_cluster(cluster); + } + + void collect_dependencies(std::vector& tables) const override + { + m_left->collect_dependencies(tables); + } + + // 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 + ConstTableRef 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); + } + + virtual std::string description(util::serializer::SerialisationState& state) const override + { + if (m_left) { + return m_left->description(state); + } + return ""; + } + + std::unique_ptr clone(Transaction* tr) const override + { + return make_subexpr(*this, tr); + } + +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, Transaction* tr) + : m_left(other.m_left->clone(tr)) + , m_right(other.m_right->clone(tr)) + { + } + + Operator& operator=(const Operator& other) + { + if (this != &other) { + m_left = other.m_left->clone(); + m_right = other.m_right->clone(); + } + return *this; + } + + Operator(Operator&&) noexcept = default; + Operator& operator=(Operator&&) noexcept = default; + + // See comment in base class + void set_base_table(ConstTableRef table) override + { + m_left->set_base_table(table); + m_right->set_base_table(table); + } + + void set_cluster(const Cluster* cluster) override + { + m_left->set_cluster(cluster); + m_right->set_cluster(cluster); + } + + // 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 + ConstTableRef get_base_table() const override + { + ConstTableRef l = m_left->get_base_table(); + ConstTableRef 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 bool(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); + } + + virtual std::string description(util::serializer::SerialisationState& state) const override + { + std::string s; + if (m_left) { + s += m_left->description(state); + } + s += (" " + oper::description() + " "); + if (m_right) { + s += m_right->description(state); + } + return s; + } + + std::unique_ptr clone(Transaction* tr) const override + { + return make_subexpr(*this, tr); + } + +private: + typedef typename oper::type T; + std::unique_ptr m_left; + std::unique_ptr m_right; +}; + +namespace { +template +inline Mixed get_mixed(const Value& val) +{ + return Mixed(val.m_storage[0]); +} + +template <> +inline Mixed get_mixed(const Value& val) +{ + return Mixed(int64_t(val.m_storage[0])); +} +} // namespace + +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)) + { + m_left_is_const = m_left->has_constant_evaluation(); + if (m_left_is_const) { + m_left->evaluate(-1 /*unused*/, m_left_value); + } + } + + // See comment in base class + void set_base_table(ConstTableRef table) override + { + m_left->set_base_table(table); + m_right->set_base_table(table); + } + + void set_cluster(const Cluster* cluster) override + { + if (m_has_matches) { + m_cluster = cluster; + } + else { + m_left->set_cluster(cluster); + m_right->set_cluster(cluster); + } + } + + double init() override + { + double dT = m_left_is_const ? 10.0 : 50.0; + if (std::is_same::value && m_left_is_const && m_right->has_search_index()) { + if (m_left_value.m_storage.is_null(0)) { + m_matches = m_right->find_all(Mixed()); + } + else { + m_matches = m_right->find_all(get_mixed(m_left_value)); + } + // Sort + std::sort(m_matches.begin(), m_matches.end()); + // Remove all duplicates + m_matches.erase(std::unique(m_matches.begin(), m_matches.end()), m_matches.end()); + + m_has_matches = true; + m_index_get = 0; + m_index_end = m_matches.size(); + dT = 0; + } + + return dT; + } + + // 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 + ConstTableRef get_base_table() const override + { + ConstTableRef l = m_left->get_base_table(); + ConstTableRef 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; + } + + void collect_dependencies(std::vector& tables) const override + { + m_left->collect_dependencies(tables); + m_right->collect_dependencies(tables); + } + + size_t find_first(size_t start, size_t end) const override + { + if (m_has_matches) { + if (m_index_get < m_index_end && start < end) { + ObjKey first_key = m_cluster->get_real_key(start); + auto actual_key = m_matches[m_index_get]; + + // skip through keys which are in "earlier" leafs than the one selected by start..end: + while (first_key > actual_key) { + m_index_get++; + if (m_index_get == m_index_end) + return not_found; + actual_key = m_matches[m_index_get]; + } + // if actual key is bigger than last key, it is not in this leaf + ObjKey last_key = m_cluster->get_real_key(end - 1); + if (actual_key > last_key) + return not_found; + + // key is known to be in this leaf, so find key whithin leaf keys + return m_cluster->lower_bound_key(ObjKey(actual_key.value - m_cluster->get_offset())); + + } + return not_found; + } + + size_t match; + + Value left; + Value right; + + for (; start < end;) { + if (m_left_is_const) { + m_right->evaluate(start, right); + match = Value::template compare_const(&m_left_value, &right); + } + else { + 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 + } + + virtual std::string description(util::serializer::SerialisationState& state) const override + { + if (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) { + // these string conditions have the arguments reversed but the order is important + // operations ==, and != can be reversed because the produce the same results both ways + return util::serializer::print_value(m_right->description(state) + " " + TCond::description() + " " + + m_left->description(state)); + } + return util::serializer::print_value(m_left->description(state) + " " + TCond::description() + " " + + m_right->description(state)); + } + + std::unique_ptr clone(Transaction* tr) const override + { + return std::unique_ptr(new Compare(*this, tr)); + } + +private: + Compare(const Compare& other, Transaction* tr) + : m_left(other.m_left->clone(tr)) + , m_right(other.m_right->clone(tr)) + , m_left_is_const(other.m_left_is_const) + { + if (m_left_is_const) { + m_left->evaluate(-1 /*unused*/, m_left_value); + } + } + + std::unique_ptr m_left; + std::unique_ptr m_right; + const Cluster* m_cluster; + bool m_left_is_const; + Value m_left_value; + bool m_has_matches = false; + std::vector m_matches; + mutable size_t m_index_get = 0; + size_t m_index_end = 0; +}; +} +#endif // REALM_QUERY_EXPRESSION_HPP diff --git a/src/vendor-include/realm-ios/include/realm/realm_nmmintrin.h b/src/vendor-include/realm-ios/include/realm/realm_nmmintrin.h new file mode 100644 index 000000000..8144da61d --- /dev/null +++ b/src/vendor-include/realm-ios/include/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/src/vendor-include/realm-ios/include/realm/replication.hpp b/src/vendor-include/realm-ios/include/realm/replication.hpp new file mode 100644 index 000000000..047164ed9 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/replication.hpp @@ -0,0 +1,563 @@ +/************************************************************************* + * + * 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 { +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() const = 0; + + /// Called during construction of the associated SharedGroup object. + /// + /// \param shared_group The assocoated SharedGroup object. + virtual void initialize(DB& 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(Group& group, version_type current_version, bool history_updated); + version_type prepare_commit(version_type current_version); + void finalize_commit() noexcept; + void abort_transact() noexcept; + + //@} + + /// Get the list of uncommitted changes accumulated so far in the current + /// write transaction. + /// + /// The callee retains ownership of the referenced memory. The ownership is + /// not handed over to 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() const noexcept = 0; + + /// 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; + + /// 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 + /// used by writers. All writers can share the same object as all write + /// transactions are serialized. + /// + /// This function must return null when, and only when get_history_type() + /// returns \ref hist_None. + virtual _impl::History* _get_history_write() = 0; + + /// Returns an object that gives access to the history of changesets in a + /// way that allows for continuous transactions to work. All readers must + /// get their own exclusive object as readers are not blocking each other. + /// (Group::advance_transact() in particular). + /// + /// This function must return null when, and only when get_history_type() + /// returns \ref hist_None. + virtual std::unique_ptr<_impl::History> _create_history_read() = 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; + + template + void set(const Table*, ColKey col_key, ObjKey key, T value, _impl::Instruction variant); + + ~Replication() override; + +protected: + DB* m_db = nullptr; + Replication(_impl::TransactLogStream& stream); + + void register_db(DB* owner) + { + m_db = owner; + } + + //@{ + + /// 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(Group& group, 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; + friend class DB; +}; + +class Replication::Interrupted : public std::exception { +public: + const char* what() const noexcept override + { + return "Interrupted"; + } +}; + + +class TrivialReplication : public Replication { +public: + ~TrivialReplication() noexcept + { + } + + std::string get_database_path() const override; + +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; + + BinaryData get_uncommitted_changes() const noexcept override; + + void initialize(DB&) override; + void do_initiate_transact(Group& group, 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; + +private: + const std::string m_database_file; + _impl::TransactLogBufferStream m_stream; + + size_t transact_log_size(); +}; + + +// Implementation: + +inline Replication::Replication(_impl::TransactLogStream& stream) + : _impl::TransactLogConvenientEncoder(stream) +{ +} + +inline void Replication::initiate_transact(Group& group, version_type current_version, bool history_updated) +{ + if (auto hist = _get_history_write()) { + hist->set_group(&group, history_updated); + } + do_initiate_transact(group, 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; +} + +template <> +inline void Replication::set(const Table* table, ColKey col_key, ObjKey key, StringData value, + _impl::Instruction variant) +{ + set_string(table, col_key, key, value, variant); +} + +template <> +inline void Replication::set(const Table* table, ColKey col_key, ObjKey key, BinaryData value, + _impl::Instruction variant) +{ + set_binary(table, col_key, key, value, variant); +} + +template <> +inline void Replication::set(const Table* table, ColKey col_key, ObjKey key, Timestamp value, + _impl::Instruction variant) +{ + if (value.is_null()) { + set_null(table, col_key, key, variant); + } + else { + set_timestamp(table, col_key, key, value, variant); + } +} + +template <> +inline void Replication::set(const Table* table, ColKey col_key, ObjKey key, bool value, _impl::Instruction variant) +{ + set_bool(table, col_key, key, value, variant); +} + +template <> +inline void Replication::set(const Table* table, ColKey col_key, ObjKey key, float value, _impl::Instruction variant) +{ + set_float(table, col_key, key, value, variant); +} + +template <> +inline void Replication::set(const Table* table, ColKey col_key, ObjKey key, double value, _impl::Instruction variant) +{ + set_double(table, col_key, key, value, variant); +} + +template <> +inline void Replication::set(const Table* table, ColKey col_key, ObjKey key, ObjKey target_key, + _impl::Instruction variant) +{ + if (target_key) { + set_link(table, col_key, key, target_key, variant); + } + else { + nullify_link(table, col_key, key); + } +} + +template <> +void Replication::set(const Table* table, ColKey col_key, ObjKey key, Mixed value, _impl::Instruction variant); + +inline TrivialReplication::TrivialReplication(const std::string& database_file) + : Replication(m_stream) + , m_database_file(database_file) +{ +} + +inline BinaryData TrivialReplication::get_uncommitted_changes() const noexcept +{ + const char* data = m_stream.get_data(); + size_t size = write_position() - data; + return BinaryData(data, size); +} + +inline size_t TrivialReplication::transact_log_size() +{ + return write_position() - m_stream.get_data(); +} + +} // namespace realm + +#endif // REALM_REPLICATION_HPP diff --git a/src/vendor-include/realm-ios/include/realm/sort_descriptor.hpp b/src/vendor-include/realm-ios/include/realm/sort_descriptor.hpp new file mode 100644 index 000000000..8d3a2685f --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/sort_descriptor.hpp @@ -0,0 +1,328 @@ +/************************************************************************* + * + * 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_SORT_DESCRIPTOR_HPP +#define REALM_SORT_DESCRIPTOR_HPP + +#include +#include +#include +#include + +namespace realm { + +class SortDescriptor; +class ConstTableRef; +class Group; + +enum class DescriptorType { Sort, Distinct, Limit, Include }; + +struct LinkPathPart { + // Constructor for forward links + LinkPathPart(ColKey col_key) + : column_key(col_key) + { + } + // Constructor for backward links. Source table must be a valid table. + LinkPathPart(ColKey col_key, ConstTableRef source); + // Each step in the path can be a forward or a backward link. + // In case of a backlink, the column_key indicates the origin link column + // (the forward link column in the origin table), not the backlink column + // itself. + ColKey column_key; + // "from" is omitted to indicate forward links, if it is valid then + // this path describes a backlink originating from the column from[column_key] + TableKey from; +}; + +class BaseDescriptor { +public: + struct IndexPair { + IndexPair(ObjKey k, size_t i) + : key_for_object(k) + , index_in_view(i) + { + } + bool operator<(const IndexPair& other) const + { + return index_in_view < other.index_in_view; + } + ObjKey key_for_object; + size_t index_in_view; + Mixed cached_value; + }; + class IndexPairs : public std::vector { + public: + size_t m_removed_by_limit = 0; + }; + class Sorter { + public: + Sorter(std::vector> const& columns, std::vector const& ascending, + Table const& root_table, const IndexPairs& indexes); + Sorter() + { + } + + bool operator()(IndexPair i, IndexPair j, bool total_ordering = true) const; + + bool has_links() const + { + return std::any_of(m_columns.begin(), m_columns.end(), + [](auto&& col) { return !col.translated_keys.empty(); }); + } + + bool any_is_null(IndexPair i) const + { + return std::any_of(m_columns.begin(), m_columns.end(), [=](auto&& col) { + return col.is_null.empty() ? false : col.is_null[i.index_in_view]; + }); + } + void cache_first_column(IndexPairs& v); + + private: + struct SortColumn { + SortColumn(const Table* t, ColKey c, bool a) + : table(t) + , col_key(c) + , ascending(a) + { + } + std::vector is_null; + std::vector translated_keys; + + const Table* table; + ColKey col_key; + bool ascending; + }; + std::vector m_columns; + friend class ObjList; + }; + + BaseDescriptor() = default; + virtual ~BaseDescriptor() = default; + virtual bool is_valid() const noexcept = 0; + virtual std::string get_description(ConstTableRef attached_table) const = 0; + virtual std::unique_ptr clone() const = 0; + virtual DescriptorType get_type() const = 0; + virtual void collect_dependencies(const Table* table, std::vector& table_keys) const = 0; + virtual Sorter sorter(Table const& table, const IndexPairs& indexes) const = 0; + // Do what you have to do + virtual void execute(IndexPairs& v, const Sorter& predicate, const BaseDescriptor* next) const = 0; +}; + + +// ColumnsDescriptor encapsulates a reference to a set of columns (possibly over +// links), which is used to indicate the criteria columns for sort and distinct. +class ColumnsDescriptor : public BaseDescriptor { +public: + ColumnsDescriptor() = default; + + // Create a descriptor for the given columns on the given table. + // Each vector in `column_keys` represents a chain of columns, where + // all but the last are Link columns (n.b.: LinkList and Backlink are not + // supported), and the final is any column type that can be sorted on. + // `column_keys` must be non-empty, and each vector within it must also + // be non-empty. + ColumnsDescriptor(std::vector> column_keys); + + // returns whether this descriptor is valid and can be used for sort or distinct + bool is_valid() const noexcept override + { + return !m_column_keys.empty(); + } + void collect_dependencies(const Table* table, std::vector& table_keys) const override; + +protected: + std::vector> m_column_keys; +}; + +class DistinctDescriptor : public ColumnsDescriptor { +public: + DistinctDescriptor() = default; + DistinctDescriptor(std::vector> column_keys) + : ColumnsDescriptor(column_keys) + { + } + + std::unique_ptr clone() const override; + + DescriptorType get_type() const override + { + return DescriptorType::Distinct; + } + + Sorter sorter(Table const& table, const IndexPairs& indexes) const override; + void execute(IndexPairs& v, const Sorter& predicate, const BaseDescriptor* next) const override; + + std::string get_description(ConstTableRef attached_table) const override; +}; + + +class SortDescriptor : public ColumnsDescriptor { +public: + // Create a sort descriptor for the given columns on the given table. + // See ColumnsDescriptor for restrictions on `column_keys`. + // The sort order can be specified by using `ascending` which must either be + // empty or have one entry for each column index chain. + SortDescriptor(std::vector> column_indices, std::vector ascending = {}); + SortDescriptor() = default; + ~SortDescriptor() = default; + std::unique_ptr clone() const override; + + DescriptorType get_type() const override + { + return DescriptorType::Sort; + } + + util::Optional is_ascending(size_t ndx) const + { + if (ndx < m_ascending.size()) { + return util::Optional(m_ascending[ndx]); + } + return util::none; + } + + void merge_with(SortDescriptor&& other); + + Sorter sorter(Table const& table, const IndexPairs& indexes) const override; + + void execute(IndexPairs& v, const Sorter& predicate, const BaseDescriptor* next) const override; + + std::string get_description(ConstTableRef attached_table) const override; + +private: + std::vector m_ascending; +}; + +class LimitDescriptor : public BaseDescriptor { +public: + LimitDescriptor(size_t limit) + : m_limit(limit) + { + } + LimitDescriptor() = default; + ~LimitDescriptor() = default; + + bool is_valid() const noexcept override + { + return m_limit != size_t(-1); + } + std::string get_description(ConstTableRef attached_table) const override; + std::unique_ptr clone() const override; + size_t get_limit() const noexcept + { + return m_limit; + } + + DescriptorType get_type() const override + { + return DescriptorType::Limit; + } + + Sorter sorter(Table const&, const IndexPairs&) const override + { + return Sorter(); + } + + void collect_dependencies(const Table*, std::vector&) const override + { + } + void execute(IndexPairs& v, const Sorter& predicate, const BaseDescriptor* next) const override; + +private: + size_t m_limit = size_t(-1); +}; + +class IncludeDescriptor : public ColumnsDescriptor { +public: + IncludeDescriptor() = default; + // This constructor may throw an InvalidPathError exception if the path is not valid. + // A valid path consists of any number of connected link/list/backlink paths and always ends with a backlink + // column. + IncludeDescriptor(ConstTableRef table, const std::vector>& link_paths); + ~IncludeDescriptor() = default; + std::string get_description(ConstTableRef attached_table) const override; + std::unique_ptr clone() const override; + DescriptorType get_type() const override + { + return DescriptorType::Include; + } + void append(const IncludeDescriptor& other); + void report_included_backlinks( + ConstTableRef origin, ObjKey object, + util::FunctionRef&)> reporter) const; + + Sorter sorter(Table const&, const IndexPairs&) const override + { + return Sorter(); + } + + void collect_dependencies(const Table*, std::vector&) const override + { + } + void execute(IndexPairs& v, const Sorter& predicate, const BaseDescriptor* next) const override; + +private: + std::vector> m_backlink_sources; // stores a default TableKey for non-backlink columns +}; + + +class DescriptorOrdering { +public: + DescriptorOrdering() = default; + DescriptorOrdering(const DescriptorOrdering&); + DescriptorOrdering(DescriptorOrdering&&) = default; + DescriptorOrdering& operator=(const DescriptorOrdering&); + DescriptorOrdering& operator=(DescriptorOrdering&&) = default; + + void append_sort(SortDescriptor sort); + void append_distinct(DistinctDescriptor distinct); + void append_limit(LimitDescriptor limit); + void append_include(IncludeDescriptor include); + realm::util::Optional get_min_limit() const; + /// Remove all LIMIT statements from this descriptor ordering, returning the + /// minimum LIMIT value that existed. If there was no LIMIT statement, + /// returns `none`. + util::Optional remove_all_limits(); + bool will_limit_to_zero() const; + DescriptorType get_type(size_t index) const; + bool is_empty() const + { + return m_descriptors.empty(); + } + size_t size() const + { + return m_descriptors.size(); + } + const BaseDescriptor* operator[](size_t ndx) const; + bool will_apply_sort() const; + bool will_apply_distinct() const; + bool will_apply_limit() const; + bool will_apply_include() const; + std::string get_description(ConstTableRef target_table) const; + IncludeDescriptor compile_included_backlinks() const; + void collect_dependencies(const Table* table); + void get_versions(const Group* group, TableVersions& versions) const; + +private: + std::vector> m_descriptors; + std::vector m_dependencies; +}; +} + +#endif /* REALM_SORT_DESCRIPTOR_HPP */ diff --git a/src/vendor-include/realm-ios/include/realm/spec.hpp b/src/vendor-include/realm-ios/include/realm/spec.hpp new file mode 100644 index 000000000..c69b2ef35 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/spec.hpp @@ -0,0 +1,279 @@ +/************************************************************************* + * + * 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 +#include + +namespace realm { + +class Table; +class Group; + +class Spec { +public: + ~Spec() noexcept; + + Allocator& get_alloc() const noexcept; + + bool has_strong_link_columns() noexcept; + + // insert column at index + void insert_column(size_t column_ndx, ColKey column_key, ColumnType type, StringData name, + int attr = col_attr_None); + ColKey get_key(size_t column_ndx) const; + void rename_column(size_t column_ndx, StringData new_name); + + /// Erase the column at the specified 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); + + // 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 + ColumnAttrMask get_column_attr(size_t column_ndx) const noexcept; + + // Auto Enumerated string columns + void upgrade_string_to_enum(size_t column_ndx, ref_type keys_ref); + size_t _get_enumkeys_ndx(size_t column_ndx) const noexcept; + bool is_string_enum_type(size_t column_ndx) const noexcept; + ref_type get_enumkeys_ref(size_t column_ndx, ArrayParent*& keys_parent) noexcept; + + //@{ + /// Compare two table specs for equality. + bool operator==(const Spec&) const noexcept; + bool operator!=(const Spec&) const noexcept; + //@} + + void detach() 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 + ArrayStringShort m_names; // 2nd slot in m_top + ArrayInteger m_attr; // 3rd slot in m_top + Array m_oldsubspecs; // 4th slot in m_top + Array m_enumkeys; // 5th slot in m_top + ArrayInteger m_keys; // 6th slot in m_top + size_t m_num_public_columns; + bool m_has_strong_link_columns; + + Spec(Allocator&) noexcept; // Unattached + + bool init(ref_type) noexcept; + void init(MemRef) noexcept; + void update_internals() noexcept; + + // Returns true in case the ref has changed. + bool 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. + bool update_from_parent(size_t old_baseline) noexcept; + + void set_parent(ArrayParent*, size_t ndx_in_parent) noexcept; + + void set_column_attr(size_t column_ndx, ColumnAttrMask attr); + + // Migration + bool convert_column_attributes(); + bool convert_column_keys(TableKey table_key); + bool has_subspec() + { + return m_oldsubspecs.is_attached(); + } + void destroy_subspec() + { + m_oldsubspecs.destroy(); + m_top.set(3, 0); + } + TableKey get_opposite_link_table_key(size_t column_ndx) const noexcept; + size_t get_origin_column_ndx(size_t backlink_col_ndx) const noexcept; + ColKey find_backlink_column(TableKey origin_table_key, size_t spec_ndx) const noexcept; + + + // Generate a column key only from state in the spec. + ColKey generate_converted_colkey(size_t column_ndx, TableKey table_key); + /// Construct an empty spec and return just the reference to the + /// underlying memory. + static MemRef create_empty_spec(Allocator&); + + size_t get_subspec_ndx(size_t column_ndx) const noexcept; + + friend class Group; + friend class Table; +}; + +// 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; +} + +// 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_oldsubspecs(alloc) + , m_enumkeys(alloc) + , m_keys(alloc) +{ +} + +inline bool Spec::init_from_parent() noexcept +{ + ref_type ref = m_top.get_ref_from_parent(); + return 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 +{ + return m_num_public_columns; +} + +inline ColumnType Spec::get_column_type(size_t ndx) const noexcept +{ + REALM_ASSERT(ndx < get_column_count()); + ColumnType type = ColumnType(m_types.get(ndx)); + return type; +} + +inline ColumnAttrMask Spec::get_column_attr(size_t ndx) const noexcept +{ + REALM_ASSERT(ndx < get_column_count()); + return ColumnAttrMask(m_attr.get(ndx)); +} + +inline void Spec::set_column_attr(size_t column_ndx, ColumnAttrMask 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.m_value); + + update_internals(); +} + +inline StringData Spec::get_column_name(size_t ndx) const noexcept +{ + 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::operator!=(const Spec& s) const noexcept +{ + return !(*this == s); +} + +} // namespace realm + +#endif // REALM_SPEC_HPP diff --git a/src/vendor-include/realm-ios/include/realm/string_data.hpp b/src/vendor-include/realm-ios/include/realm/string_data.hpp new file mode 100644 index 000000000..0ddab21aa --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/string_data.hpp @@ -0,0 +1,417 @@ +/************************************************************************* + * + * 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 { + +/// Selects CityHash64 on 64-bit platforms, and Murmur2 on 32-bit platforms. +/// This is what libc++ does, and it is a good general choice for a +/// non-cryptographic hash function (suitable for std::unordered_map etc.). +size_t murmur2_or_cityhash(const unsigned char* data, size_t len) noexcept; + +uint_least32_t murmur2_32(const unsigned char* data, size_t len) noexcept; +uint_least64_t cityhash_64(const unsigned char* data, size_t len) noexcept; + + +/// 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; + + StringData(int) = delete; + + /// 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; + + /// If the StringData is NULL, the hash is 0. Otherwise, the function + /// `murmur2_or_cityhash()` is called on the data. + size_t hash() 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) +{ + if (d.is_null()) { + out << ""; + } + else { + 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(); +} + +inline size_t StringData::hash() const noexcept +{ + if (is_null()) + return 0; + auto unsigned_data = reinterpret_cast(m_data); + return murmur2_or_cityhash(unsigned_data, m_size); +} + +} // namespace realm + +namespace std { +template <> +struct hash<::realm::StringData> { + inline size_t operator()(const ::realm::StringData& str) const noexcept + { + return str.hash(); + } +}; +} // namespace std + +#endif // REALM_STRING_HPP diff --git a/src/vendor-include/realm-ios/include/realm/sync/changeset.hpp b/src/vendor-include/realm-ios/include/realm/sync/changeset.hpp new file mode 100644 index 000000000..771415a0e --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/sync/changeset.hpp @@ -0,0 +1,701 @@ +/************************************************************************* + * + * REALM CONFIDENTIAL + * __________________ + * + * [2011] - [2017] 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_CHANGESET_HPP +#define REALM_SYNC_CHANGESET_HPP + +#include +#include +#include +#include + +#include + +namespace realm { +namespace sync { + +using InternStrings = util::metered::vector; + +struct BadChangesetError : ExceptionWithBacktrace { + const char* m_message; + BadChangesetError() : BadChangesetError("Bad changeset") {} + BadChangesetError(const char* msg) : m_message(msg) {} + const char* message() const noexcept override + { + return m_message; + } +}; + +struct Changeset { + struct Range; + using timestamp_type = uint_fast64_t; + using file_ident_type = uint_fast64_t; + using version_type = uint_fast64_t; // FIXME: Get from `History`. + using StringBuffer = util::BasicStringBuffer; + + Changeset(); + struct share_buffers_tag {}; + Changeset(const Changeset&, share_buffers_tag); + Changeset(Changeset&&) = default; + Changeset& operator=(Changeset&&) = default; + Changeset(const Changeset&) = delete; + Changeset& operator=(const Changeset&) = delete; + + InternString intern_string(StringData); // Slow! + InternString find_string(StringData) const noexcept; // Slow! + StringData string_data() const noexcept; + + StringBuffer& string_buffer() noexcept; + const StringBuffer& string_buffer() const noexcept; + const InternStrings& interned_strings() const noexcept; + InternStrings& interned_strings() noexcept; + + StringBufferRange get_intern_string(InternString) const noexcept; + util::Optional try_get_intern_string(InternString) const noexcept; + util::Optional try_get_string(StringBufferRange) const noexcept; + StringData get_string(StringBufferRange) const noexcept; + StringData get_string(InternString) const noexcept; + StringBufferRange append_string(StringData); + + /// Mark the changeset as "dirty" (i.e. modified by the merge algorithm). + void set_dirty(bool dirty = true) noexcept; + + /// Whether or not the changeset is "dirty" (i.e. has been modified by the + /// merge algorithm). + bool is_dirty() const noexcept; + + // Interface to imitate std::vector: + template struct IteratorImpl; + using iterator = IteratorImpl; + using const_iterator = IteratorImpl; + using value_type = Instruction; + iterator begin() noexcept; + iterator end() noexcept; + const_iterator begin() const noexcept; + const_iterator end() const noexcept; + const_iterator cbegin() const noexcept; + const_iterator cend() const noexcept; + bool empty() const noexcept; + + /// Size of the Changeset, not counting tombstones. + /// + /// FIXME: This is an O(n) operation. + size_t size() const noexcept; + + void clear() noexcept; + + //@{ + /// Insert instructions, invalidating all iterators. + iterator insert(const_iterator pos, Instruction); + template + iterator insert(const_iterator pos, InputIt begin, InputIt end); + //@} + + /// Erase an instruction, invalidating all iterators. + iterator erase(const_iterator); + + /// Insert an instruction at the end, invalidating all iterators. + void push_back(const Instruction&); + + //@{ + /// Insert instructions at \a position without invalidating other + /// iterators. + /// + /// Only iterators created before any call to `insert_stable()` may be + /// considered stable across calls to `insert_stable()`. In addition, + /// "iterator stability" has a very specific meaning here: Other copies of + /// \a position in the program will point to the newly inserted elements + /// after calling `insert_stable()`, rather than point to the value at the + /// position prior to insertion. This is different from, say, a tree + /// structure, where iterator stability signifies the property that + /// iterators keep pointing to the same element after insertion before or + /// after that position. + /// + /// For the purpose of supporting `ChangesetIndex`, and the OT merge + /// algorithm, these semantics are acceptable, since prepended instructions + /// can never create new object or table references. + iterator insert_stable(const_iterator position, Instruction); + template + iterator insert_stable(const_iterator position, InputIt begin, InputIt end); + //@} + + /// Erase instruction at \a position without invalidating other iterators. + /// If erasing the object would invalidate other iterators, it is turned + /// into a tombstone instead, and subsequent derefencing of the iterator + /// will return `nullptr`. An iterator pointing to a tombstone remains valid + /// and can be incremented. + /// + /// Only iterators created before any call to `insert_stable()` may be + /// considered stable across calls to `erase_stable()`. If other copies of + /// \a position exist in the program, they will either point to the + /// subsequent element if that element was previously inserted with + /// `insert_stable()`, or otherwise it will be turned into a tombstone. + iterator erase_stable(const_iterator position); + +#if REALM_DEBUG + struct Reflector; + struct Printer; + void verify() const; + void print(std::ostream&) const; + void print() const; // prints to std::err +#endif + + /// The version that this changeset produced. Note: This may not be the + /// version produced by this changeset on the client on which this changeset + /// originated, but may for instance be the version produced on the server + /// after receiving and re-sending this changeset to another client. + /// + /// FIXME: The explanation above is confusing. The truth is that if this + /// changeset was received by a client from the server, then \a version is + /// the version that was produced on the server by this changeset. + /// + /// FIXME: This property, as well as \a last_integrated_remote_version, \a + /// origin_timestamp, and \a origin_file_ident should probably be removed + /// from this class, as they are not a logical part of a changeset, and also + /// are difficult to document without knowing more about what context the + /// changeset object occurs. Also, functions such as + /// InstructionApplier::apply() that a changeset as argument, but do not + /// care about those properties. + version_type version = 0; + + /// On clients, the last integrated server version. On the server, this is + /// the last integrated client version. + /// + /// FIXME: The explanation above is confusing. The truth is that if this + /// changeset was received by a client from the server, then \a + /// last_integrated_remote_version is the last client version that was + /// integrated by the server at the server version referencened by \a + /// version. + version_type last_integrated_remote_version = 0; + + /// Timestamp at origin when the original untransformed changeset was + /// produced. + timestamp_type origin_timestamp = 0; + + /// The identifier of the file in the context of which the original + /// untransformed changeset was produced. + file_ident_type origin_file_ident = 0; + +private: + struct MultiInstruction { + util::metered::vector instructions; + }; + static_assert(sizeof(MultiInstruction) <= Instruction::max_instruction_size, "Instruction::max_instruction_size too low"); + + // In order to achieve iterator semi-stability (just enough to be able to + // run the merge algorithm while maintaining a ChangesetIndex), a Changeset + // is really a list of lists. A Changeset is a vector of + // `InstructionContainer`s, and each `InstructionContainer` represents 0-N + // "real" instructions. + // + // As an optimization, there is a special case for when the + // `InstructionContainer` represents exactly 1 instruction, in which case it + // is represented inside the `InstructionContainer` without any additional + // allocations or indirections. The `InstructionContainer` derived from + // the `Instruction` struct, and co-opts the `type` field such that if the + // (invalid) value of `type` is 0xff, the contents of the `Instruction` are + // instead interpreted as an instance of `MultiInstruction`, which holds + // a vector of `Instruction`s. + // + // The size of the `MultiInstruction` may also be zero, in which case it is + // considered a "tombstone" - always as a result of a call to + // `Changeset::erase_stable()`. The potential existence of these tombstones + // is the reason that the value type of `Changeset::iterator` is + // `Instruction*`, rather than `Instruction&`. + // + // FIXME: It would be better if `Changeset::iterator::value_type` could be + // `util::Optional`, but this is prevented by a bug in + // `util::Optional`. + struct InstructionContainer : Instruction { + InstructionContainer(); + InstructionContainer(const Instruction& instr); + InstructionContainer(InstructionContainer&&) noexcept; + InstructionContainer(const InstructionContainer&); + ~InstructionContainer(); + InstructionContainer& operator=(InstructionContainer&&) noexcept; + InstructionContainer& operator=(const InstructionContainer&); + + bool is_multi() const noexcept; + void convert_to_multi(); + void insert(size_t position, Instruction instr); + void erase(size_t position); + size_t size() const noexcept; + bool is_empty() const noexcept; + + Instruction& at(size_t pos) noexcept; + const Instruction& at(size_t pos) const noexcept; + + MultiInstruction& get_multi() noexcept; + const MultiInstruction& get_multi() const noexcept; + }; + + util::metered::vector m_instructions; + std::shared_ptr m_string_buffer; + std::shared_ptr m_strings; + bool m_is_dirty = false; + + iterator const_iterator_to_iterator(const_iterator); +}; + +/// An iterator type that hides the implementation details of the support for +/// iterator stability. +/// +/// A `Changeset::iterator` is composed of an +/// `std::vector::iterator` and a `size_t` representing +/// the index into the current `InstructionContainer`. If that container is +/// empty, and the position is zero, the iterator is pointing to a tombstone. +template +struct Changeset::IteratorImpl { + using list_type = util::metered::vector; + using inner_iterator_type = std::conditional_t; + + // reference_type is a pointer because we have no way to create a reference + // to a tombstone instruction. Alternatively, it could have been + // `util::Optional`, but that runs into other issues. + using reference_type = std::conditional_t; + + using pointer_type = std::conditional_t; + using difference_type = std::ptrdiff_t; + + IteratorImpl() : m_pos(0) {} + template + IteratorImpl(const IteratorImpl& other, std::enable_if_t* = nullptr) + : m_inner(other.m_inner), m_pos(other.m_pos) {} + IteratorImpl(inner_iterator_type inner, size_t pos = 0) : m_inner(inner), m_pos(pos) {} + + inline IteratorImpl& operator++() + { + ++m_pos; + if (m_pos >= m_inner->size()) { + ++m_inner; + m_pos = 0; + } + return *this; + } + + IteratorImpl operator++(int) + { + auto copy = *this; + ++(*this); + return copy; + } + + IteratorImpl& operator--() + { + if (m_pos == 0) { + --m_inner; + m_pos = m_inner->size(); + if (m_pos != 0) + --m_pos; + } + else { + --m_pos; + } + return *this; + } + + IteratorImpl operator--(int) + { + auto copy = *this; + --(*this); + return copy; + } + + reference_type operator*() const + { + if (m_inner->size()) { + return &m_inner->at(m_pos); + } + // It was a tombstone. + return nullptr; + } + + pointer_type operator->() const + { + if (m_inner->size()) { + return &m_inner->at(m_pos); + } + // It was a tombstone. + return nullptr; + } + + bool operator==(const IteratorImpl& other) const + { + return m_inner == other.m_inner && m_pos == other.m_pos; + } + + bool operator!=(const IteratorImpl& other) const + { + return !(*this == other); + } + + bool operator<(const IteratorImpl& other) const + { + if (m_inner == other.m_inner) + return m_pos < other.m_pos; + return m_inner < other.m_inner; + } + + bool operator<=(const IteratorImpl& other) const + { + if (m_inner == other.m_inner) + return m_pos <= other.m_pos; + return m_inner < other.m_inner; + } + + bool operator>(const IteratorImpl& other) const + { + if (m_inner == other.m_inner) + return m_pos > other.m_pos; + return m_inner > other.m_inner; + } + + bool operator>=(const IteratorImpl& other) const + { + if (m_inner == other.m_inner) + return m_pos >= other.m_pos; + return m_inner > other.m_inner; + } + + inner_iterator_type m_inner; + size_t m_pos; +}; + +struct Changeset::Range { + iterator begin; + iterator end; +}; + +#if REALM_DEBUG +struct Changeset::Reflector { + struct Tracer { + virtual void name(StringData) = 0; + virtual void field(StringData, StringData) = 0; + virtual void field(StringData, GlobalKey) = 0; + virtual void field(StringData, int64_t) = 0; + virtual void field(StringData, double) = 0; + virtual void after_each() {} + virtual void before_each() {} + }; + + Reflector(Tracer& tracer, const Changeset& log) : + m_tracer(tracer), m_log(log) + {} + + void visit_all() const; +private: + Tracer& m_tracer; + const Changeset& m_log; + + friend struct Instruction; // permit access for visit() +#define REALM_DEFINE_REFLECTOR_VISITOR(X) void operator()(const Instruction::X&) const; + REALM_FOR_EACH_INSTRUCTION_TYPE(REALM_DEFINE_REFLECTOR_VISITOR) +#undef REALM_DEFINE_REFLECTOR_VISITOR +}; + +struct Changeset::Printer : Changeset::Reflector::Tracer { + explicit Printer(std::ostream& os) : m_out(os) + {} + + // ChangesetReflector::Tracer interface: + void name(StringData) final; + void field(StringData, StringData) final; + void field(StringData, GlobalKey) final; + void field(StringData, int64_t) final; + void field(StringData, double) final; + void after_each() final; + +private: + std::ostream& m_out; + bool m_first = true; + void pad_or_ellipsis(StringData, int width) const; + void print_field(StringData name, std::string value); +}; +#endif // REALM_DEBUG + + + +/// Implementation: + +inline Changeset::iterator Changeset::begin() noexcept +{ + return m_instructions.begin(); +} + +inline Changeset::iterator Changeset::end() noexcept +{ + return m_instructions.end(); +} + +inline Changeset::const_iterator Changeset::begin() const noexcept +{ + return m_instructions.begin(); +} + +inline Changeset::const_iterator Changeset::end() const noexcept +{ + return m_instructions.end(); +} + +inline Changeset::const_iterator Changeset::cbegin() const noexcept +{ + return m_instructions.cbegin(); +} + +inline Changeset::const_iterator Changeset::cend() const noexcept +{ + return m_instructions.end(); +} + +inline bool Changeset::empty() const noexcept +{ + return size() == 0; +} + +inline size_t Changeset::size() const noexcept +{ + size_t sum = 0; + for (auto& x: m_instructions) + sum += x.size(); + return sum; +} + +inline void Changeset::clear() noexcept +{ + m_instructions.clear(); +} + +inline util::Optional Changeset::try_get_intern_string(InternString string) const noexcept +{ + if (string.value >= m_strings->size()) + return util::none; + return (*m_strings)[string.value]; +} + +inline StringBufferRange Changeset::get_intern_string(InternString string) const noexcept +{ + auto str = try_get_intern_string(string); + REALM_ASSERT(str); + return *str; +} + +inline InternStrings& Changeset::interned_strings() noexcept +{ + return *m_strings; +} + +inline const InternStrings& Changeset::interned_strings() const noexcept +{ + return *m_strings; +} + +inline auto Changeset::string_buffer() noexcept -> StringBuffer& +{ + return *m_string_buffer; +} + +inline auto Changeset::string_buffer() const noexcept -> const StringBuffer& +{ + return *m_string_buffer; +} + +inline util::Optional Changeset::try_get_string(StringBufferRange range) const noexcept +{ + if (range.offset > m_string_buffer->size()) + return util::none; + if (range.offset + range.size > m_string_buffer->size()) + return util::none; + return StringData{m_string_buffer->data() + range.offset, range.size}; +} + +inline StringData Changeset::get_string(StringBufferRange range) const noexcept +{ + auto string = try_get_string(range); + REALM_ASSERT(string); + return *string; +} + +inline StringData Changeset::get_string(InternString string) const noexcept +{ + return get_string(get_intern_string(string)); +} + +inline StringData Changeset::string_data() const noexcept +{ + return StringData{m_string_buffer->data(), m_string_buffer->size()}; +} + +inline StringBufferRange Changeset::append_string(StringData string) +{ + m_string_buffer->reserve(1024); // we expect more strings + size_t offset = m_string_buffer->size(); + m_string_buffer->append(string.data(), string.size()); + return StringBufferRange{uint32_t(offset), uint32_t(string.size())}; +} + +inline bool Changeset::is_dirty() const noexcept +{ + return m_is_dirty; +} + +inline void Changeset::set_dirty(bool dirty) noexcept +{ + m_is_dirty = dirty; +} + +inline Changeset::iterator Changeset::insert(const_iterator pos, Instruction instr) +{ + Instruction* p = &instr; + return insert(pos, p, p + 1); +} + +template +inline Changeset::iterator Changeset::insert(const_iterator pos, InputIt begin, InputIt end) +{ + if (pos.m_pos == 0) + return m_instructions.insert(pos.m_inner, begin, end); + return insert_stable(pos, begin, end); +} + +inline Changeset::iterator Changeset::erase(const_iterator pos) +{ + if (pos.m_inner->size() <= 1) + return m_instructions.erase(pos.m_inner); + return erase_stable(pos); +} + +inline Changeset::iterator Changeset::insert_stable(const_iterator pos, Instruction instr) +{ + Instruction* p = &instr; + return insert_stable(pos, p, p + 1); +} + +template +inline Changeset::iterator Changeset::insert_stable(const_iterator cpos, InputIt begin, InputIt end) +{ + iterator pos = const_iterator_to_iterator(cpos); + size_t i = 0; + for (auto it = begin; it != end; ++it, ++i) { + pos.m_inner->insert(pos.m_pos + i, *it); + } + return pos; +} + +inline Changeset::iterator Changeset::erase_stable(const_iterator cpos) +{ + auto pos = const_iterator_to_iterator(cpos); + auto begin = m_instructions.begin(); + auto end = m_instructions.end(); + REALM_ASSERT(pos.m_inner >= begin); + REALM_ASSERT(pos.m_inner < end); + pos.m_inner->erase(pos.m_pos); + if (pos.m_pos >= pos.m_inner->size()) { + do { + ++pos.m_inner; + } while (pos.m_inner != end && pos.m_inner->is_empty()); + pos.m_pos = 0; + } + return pos; +} + +inline void Changeset::push_back(const Instruction& instr) +{ + m_instructions.emplace_back(instr); +} + +inline auto Changeset::const_iterator_to_iterator(const_iterator cpos) -> iterator +{ + size_t offset = cpos.m_inner - m_instructions.cbegin(); + return iterator{m_instructions.begin() + offset, cpos.m_pos}; +} + +inline Changeset::InstructionContainer::~InstructionContainer() +{ + if (is_multi()) { + get_multi().~MultiInstruction(); + } + // Instruction subtypes are required to be POD-types (trivially + // destructible), and this is checked by a static_assert in + // instructions.hpp. Therefore, it is safe to do nothing if this is not a + // multi-instruction. +} + +inline bool Changeset::InstructionContainer::is_multi() const noexcept +{ + return type == Type(InstrTypeMultiInstruction); +} + +inline size_t Changeset::InstructionContainer::size() const noexcept +{ + if (is_multi()) + return get_multi().instructions.size(); + return 1; +} + +inline bool Changeset::InstructionContainer::is_empty() const noexcept +{ + if (is_multi()) { + return get_multi().instructions.empty(); + } + return false; +} + +inline Instruction& Changeset::InstructionContainer::at(size_t pos) noexcept +{ + REALM_ASSERT(pos < size()); + if (is_multi()) + return get_multi().instructions[pos]; + return *this; +} + +inline const Instruction& Changeset::InstructionContainer::at(size_t pos) const noexcept +{ + REALM_ASSERT(pos < size()); + if (is_multi()) + return get_multi().instructions[pos]; + return *this; +} + +inline Changeset::MultiInstruction& Changeset::InstructionContainer::get_multi() noexcept +{ + REALM_ASSERT(is_multi()); + return *reinterpret_cast(&m_storage); +} + +inline const Changeset::MultiInstruction& Changeset::InstructionContainer::get_multi() const noexcept +{ + REALM_ASSERT(is_multi()); + return *reinterpret_cast(&m_storage); +} + +} // namespace sync +} // namespace realm + +namespace std { + +template +struct iterator_traits> { + using difference_type = std::ptrdiff_t; + using iterator_category = std::bidirectional_iterator_tag; +}; + +} // namespace std + +#endif // REALM_SYNC_CHANGESET_HPP diff --git a/src/vendor-include/realm-ios/include/realm/sync/changeset_cooker.hpp b/src/vendor-include/realm-ios/include/realm/sync/changeset_cooker.hpp new file mode 100644 index 000000000..dacfb6a40 --- /dev/null +++ b/src/vendor-include/realm-ios/include/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 ClientReplication::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/src/vendor-include/realm-ios/include/realm/sync/changeset_encoder.hpp b/src/vendor-include/realm-ios/include/realm/sync/changeset_encoder.hpp new file mode 100644 index 000000000..92b5da07b --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/sync/changeset_encoder.hpp @@ -0,0 +1,122 @@ +/************************************************************************* + * + * REALM CONFIDENTIAL + * __________________ + * + * [2011] - [2017] 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_CHANGESET_ENCODER_HPP +#define REALM_SYNC_CHANGESET_ENCODER_HPP + +#include +#include +#include + +namespace realm { +namespace sync { + +struct ChangesetEncoder: InstructionHandler { + using Buffer = util::AppendBuffer; + + Buffer release() noexcept; + void reset() noexcept; + const Buffer& buffer() const noexcept; + InternString intern_string(StringData); + + void set_intern_string(uint32_t index, StringBufferRange) override; + // FIXME: This doesn't copy the input, but the drawback is that there can + // only be a single StringBufferRange per instruction. Luckily, no + // instructions exist that require two or more. + StringBufferRange add_string_range(StringData) override; + void operator()(const Instruction&) override; + +#define REALM_DEFINE_INSTRUCTION_HANDLER(X) void operator()(const Instruction::X&); + REALM_FOR_EACH_INSTRUCTION_TYPE(REALM_DEFINE_INSTRUCTION_HANDLER) +#undef REALM_DEFINE_INSTRUCTION_HANDLER + + void encode_single(const Changeset& log); + +protected: + template static void encode(E& encoder, const Instruction&); + + StringData get_string(StringBufferRange) const noexcept; + +private: + template + void append(Instruction::Type t, Args&&...); + void append_string(StringBufferRange); // does not intern the string + void append_bytes(const void*, size_t); + + template void append_int(T); + void append_payload(const Instruction::Payload&); + void append_value(DataType); + void append_value(bool); + void append_value(uint8_t); + void append_value(int64_t); + void append_value(uint32_t); + void append_value(uint64_t); + void append_value(float); + void append_value(double); + void append_value(InternString); + void append_value(GlobalKey); + void append_value(Timestamp); + + Buffer m_buffer; + util::metered::map m_intern_strings_rev; + StringData m_string_range; +}; + +template +void encode_changeset(const Changeset&, util::AppendBuffer& out_buffer); + + +// Implementation + +inline auto ChangesetEncoder::buffer() const noexcept -> const Buffer& +{ + return m_buffer; +} + +inline void ChangesetEncoder::operator()(const Instruction& instr) +{ + encode(*this, instr); // Throws +} + +template inline void ChangesetEncoder::encode(E& encoder, const Instruction& instr) +{ + instr.visit(encoder); // Throws +} + +inline StringData ChangesetEncoder::get_string(StringBufferRange range) const noexcept +{ + const char* data = m_string_range.data() + range.offset; + std::size_t size = std::size_t(range.size); + return StringData{data, size}; +} + +template +void encode_changeset(const Changeset& changeset, util::AppendBuffer& out_buffer) +{ + ChangesetEncoder encoder; + encoder.encode_single(changeset); // Throws + auto& buffer = encoder.buffer(); + out_buffer.append(buffer.data(), buffer.size()); // Throws +} + +} // namespace sync +} // namespace realm + +#endif // REALM_SYNC_CHANGESET_ENCODER_HPP diff --git a/src/vendor-include/realm-ios/include/realm/sync/changeset_parser.hpp b/src/vendor-include/realm-ios/include/realm/sync/changeset_parser.hpp new file mode 100644 index 000000000..f33156989 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/sync/changeset_parser.hpp @@ -0,0 +1,47 @@ +/************************************************************************* + * + * REALM CONFIDENTIAL + * __________________ + * + * [2011] - [2017] 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_CHANGESET_PARSER_HPP +#define REALM_SYNC_CHANGESET_PARSER_HPP + +#include +#include + +namespace realm { +namespace sync { + +struct ChangesetParser { + /// Throws BadChangesetError if parsing fails. + /// + /// FIXME: Consider using std::error_code instead of throwing exceptions on + /// parse errors. + void parse(_impl::NoCopyInputStream&, InstructionHandler&); +private: + struct State; +}; + +void parse_changeset(_impl::NoCopyInputStream&, Changeset& out_log); +void parse_changeset(_impl::InputStream&, Changeset& out_log); + + +} // namespace sync +} // namespace realm + +#endif // REALM_SYNC_CHANGESET_PARSER_HPP diff --git a/src/vendor-include/realm-ios/include/realm/sync/client.hpp b/src/vendor-include/realm-ios/include/realm/sync/client.hpp new file mode 100644 index 000000000..63d8cbbff --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/sync/client.hpp @@ -0,0 +1,1364 @@ +/************************************************************************* + * + * 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 +#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, + + /// For testing purposes only. + /// + /// Never reconnect automatically after the connection is closed due to + /// an error. Allow immediate reconnect if the connection was closed + /// voluntarily (e.g., due to sessions being abandoned). + /// + /// In this mode, Client::cancel_reconnect_delay() and + /// Session::cancel_reconnect_delay() can still be used to trigger + /// another reconnection attempt (with no delay) after an error has + /// caused the connection to be closed. + testing + }; + + using port_type = util::network::Endpoint::port_type; + using RoundtripTimeHandler = void(milliseconds_type roundtrip_time); + + static constexpr milliseconds_type default_connect_timeout = 120000; // 2 minutes + static constexpr milliseconds_type default_connection_linger_time = 30000; // 30 seconds + static constexpr milliseconds_type default_ping_keepalive_period = 60000; // 1 minute + static constexpr milliseconds_type default_pong_keepalive_timeout = 120000; // 2 minutes + static constexpr milliseconds_type default_fast_reconnect_limit = 60000; // 1 minute + + struct Config { + Config() {} + + /// An optional custom platform description to be sent to server as part + /// of a user agent description (HTTP `User-Agent` header). + /// + /// If left empty, the platform description will be whatever is returned + /// by util::get_platform_info(). + std::string user_agent_platform_info; + + /// Optional information about the application to be added to the user + /// agent description as sent to the server. The intention is that the + /// application describes itself using the following (rough) syntax: + /// + /// ::= ( )* + /// ::= "/" [
] + /// ::= ()+ + /// ::= ( | "." | "-" | "_")* + ///
::= + /// ::= "(" ( | )* ")" + /// + /// Where `` is a single space character, `` is a decimal + /// digit, `` is any alphanumeric character, and `` is + /// any character other than `(` and `)`. + /// + /// When multiple levels are present, the innermost layer (the one that + /// is closest to this API) should appear first. + /// + /// Example: + /// + /// RealmJS/2.13.0 RealmStudio/2.9.0 + /// + /// Note: The user agent description is not intended for machine + /// interpretation, but should still follow the specified syntax such + /// that it remains easily interpretable by human beings. + std::string user_agent_application_info; + + /// 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 either on behalf of the constructor or on behalf + /// of the invocation of run(). + util::Logger* logger = nullptr; + + /// 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 needs to be true for now, due to limitations in + /// the load balancer. + bool one_connection_per_session = true; + + /// 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. + /// + /// Many operations, such as serialized transactions, are not suppored + /// in this mode. + 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_replication(), TrivialChangesetCooker. + std::shared_ptr changeset_cooker; + + /// The maximum number of milliseconds to allow for a connection to + /// become fully established. This includes the time to resolve the + /// network address, the TCP connect operation, the SSL handshake, and + /// the WebSocket handshake. + milliseconds_type connect_timeout = default_connect_timeout; + + /// The number of milliseconds to keep a connection open after all + /// sessions have been abandoned (or suspended by errors). + /// + /// The purpose of this linger time is to avoid close/reopen cycles + /// during short periods of time where there are no sessions interested + /// in using the connection. + /// + /// If the connection gets closed due to an error before the linger time + /// expires, the connection will be kept closed until there are sessions + /// willing to use it again. + milliseconds_type connection_linger_time = default_connection_linger_time; + + /// The client will send PING messages periodically to allow the server + /// to detect dead connections (heartbeat). This parameter specifies the + /// time, in milliseconds, between these PING messages. When scheduling + /// the next PING message, the client will deduct a small random amount + /// from the specified value to help spread the load on the server from + /// many clients. + milliseconds_type ping_keepalive_period = default_ping_keepalive_period; + + /// Whenever the server receives a PING message, it is supposed to + /// respond with a PONG messsage to allow the client to detect dead + /// connections (heartbeat). This parameter specifies the time, in + /// milliseconds, that the client will wait for the PONG response + /// message before it assumes that the connection is dead, and + /// terminates it. + milliseconds_type pong_keepalive_timeout = default_pong_keepalive_timeout; + + /// The maximum amount of time, in milliseconds, since the loss of a + /// prior connection, for a new connection to be considered a *fast + /// reconnect*. + /// + /// In general, when a client establishes a connection to the server, + /// the uploading process remains suspended until the initial + /// downloading process completes (as if by invocation of + /// Session::async_wait_for_download_completion()). However, to avoid + /// unnecessary latency in change propagation during ongoing + /// application-level activity, if the new connection is established + /// less than a certain amount of time (`fast_reconnect_limit`) since + /// the client was previously connected to the server, then the + /// uploading process will be activated immediately. + /// + /// For now, the purpose of the general delaying of the activation of + /// the uploading process, is to increase the chance of multiple initial + /// transactions on the client-side, to be uploaded to, and processed by + /// the server as a single unit. In the longer run, the intention is + /// that the client should upload transformed (from reciprocal history), + /// rather than original changesets when applicable to reduce the need + /// for changeset to be transformed on both sides. The delaying of the + /// upload process will increase the number of cases where this is + /// possible. + /// + /// FIXME: Currently, the time between connections is not tracked across + /// sessions, so if the application closes its session, and opens a new + /// one immediately afterwards, the activation of the upload process + /// will be delayed unconditionally. + milliseconds_type fast_reconnect_limit = default_fast_reconnect_limit; + + /// Set to true to completely disable delaying of the upload process. In + /// this mode, the upload process will be activated immediately, and the + /// value of `fast_reconnect_limit` is ignored. + /// + /// For testing purposes only. + bool disable_upload_activation_delay = false; + + /// If `disable_upload_compaction` is true, every changeset will be + /// compacted before it is uploaded to the server. Compaction will + /// reduce the size of a changeset if the same field is set multiple + /// times or if newly created objects are deleted within the same + /// transaction. Log compaction increeses CPU usage and memory + /// consumption. + bool disable_upload_compaction = false; + + /// Set the `TCP_NODELAY` option on all TCP/IP sockets. This disables + /// the Nagle algorithm. Disabling it, can in some cases be used to + /// decrease latencies, but possibly at the expense of scalability. Be + /// sure to research the subject before you enable this option. + bool tcp_no_delay = false; + + /// The specified function will be called whenever a PONG message is + /// received on any connection. The round-trip time in milliseconds will + /// be pased to the function. The specified function will always be + /// called by the client's event loop thread, i.e., the thread that + /// calls `Client::run()`. This feature is mainly for testing purposes. + std::function roundtrip_time_handler; + + /// Disable sync to disk (fsync(), msync()) for all realm files managed + /// by this client. + /// + /// Testing/debugging feature. Should never be enabled in production. + bool disable_sync_to_disk = false; + }; + + /// \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. + /// + /// Thread-safe. + void cancel_reconnect_delay(); + + /// \brief Wait for session termination to complete. + /// + /// Wait for termination of all sessions whose termination was initiated + /// prior this call (the completion condition), or until the client's event + /// loop thread exits from Client::run(), whichever happens + /// first. Termination of a session can be initiated implicitly (e.g., via + /// destruction of the session object), or explicitly by Session::detach(). + /// + /// Note: After session termination (when this function returns true) no + /// session specific callback function can be called or continue to execute, + /// and the client is guaranteed to no longer have a Realm file open on + /// behalf of the terminated session. + /// + /// CAUTION: If run() returns while a wait operation is in progress, this + /// waiting function will return immediately, even if the completion + /// condition is not yet satisfied. The completion condition is guaranteed + /// to be satisfied only when these functions return true. If it returns + /// false, session specific callback functions may still be executing or get + /// called, and the associated Realm files may still not have been closed. + /// + /// If a new wait operation is initiated while another wait operation is in + /// progress by another thread, the waiting period of fist operation may, or + /// may not get extended. The application must not assume either. + /// + /// Note: Session termination does not imply that the client has received an + /// UNBOUND message from the server (see the protocol specification). This + /// may happen later. + /// + /// \return True only if the completion condition was satisfied. False if + /// the client's event loop thread exited from Client::run() in which case + /// the completion condition may, or may not have been satisfied. + /// + /// Note: These functions are fully thread-safe. That is, they may be called + /// by any thread, and by multiple threads concurrently. + bool wait_for_session_terminations_or_client_stopped(); + + /// Returns false if the specified URL is invalid. + bool decompose_server_url(const std::string& url, ProtocolEnvelope& protocol, + std::string& address, port_type& port, std::string& path) const; + +private: + class Impl; + std::unique_ptr m_impl; + friend class Session; +}; + + +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 session objects may +/// coexists for a single file, as long as bind() has been called on at most one +/// of them. Additionally, two bound session objects for the same file are +/// allowed to exist at different times, if they have no overlap in time (in +/// their bound state), 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 bound +/// session objects for the same local Realm file, it they are associated with +/// two different client objects that overlap in time, even if the session +/// objects do not overlap in time (in their bound state). 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. +/// +/// Callback semantics: All session specific callback functions will be executed +/// by the event loop thread, i.e., the thread that calls Client::run(). No +/// callback function will be called before Session::bind() is called. Callback +/// functions that are specified prior to calling bind() (e.g., any passed to +/// set_progress_handler()) may start to execute before bind() returns, as long +/// as some thread is executing Client::run(). Likewise, completion handlers, +/// such as those passed to async_wait_for_sync_completion() may start to +/// execute before the submitting function returns. All session specific +/// callback functions (including completion handlers) are guaranteed to no +/// longer be executing when session termination completes, and they are +/// guaranteed to not be called after session termination completes. Termination +/// is an event that completes asynchronously with respect to the application, +/// but is initiated by calling detach(), or implicitely by destroying a session +/// object. After having initiated one or more session terminations, the +/// application can wait for those terminations to complete by calling +/// Client::wait_for_session_terminations_or_client_stopped(). Since callback +/// functinos are always executed by the event loop thread, they are also +/// guaranteed to not be executing after Client::run() has returned. +class Session { +public: + using port_type = util::network::Endpoint::port_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, + std::uint_fast64_t snapshot_version); + using WaitOperCompletionHandler = std::function; + using SerialTransactChangeset = util::Buffer; + using SerialTransactInitiationHandler = std::function; + using SerialTransactCompletionHandler = std::function; + using SSLVerifyCallback = bool(const std::string& server_address, + port_type server_port, + const char* pem_data, + size_t pem_size, + int preverify_ok, + int depth); + + struct Config { + Config() {} + + /// server_address is the fully qualified host name, or IP address of + /// the server. + std::string server_address = "localhost"; + + /// server_port is the port at which the server listens. If server_port + /// is zero, the default port for the specified protocol is used. See + /// ProtocolEnvelope for information on default ports. + port_type server_port = 0; + + /// server_path is 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. + std::string server_path = "/"; + + /// The protocol used for communicating with the server. See + /// ProtocolEnvelope. + ProtocolEnvelope protocol_envelope = ProtocolEnvelope::realm; + + /// url_prefix is a prefix that is prepended to the server_path + /// in the HTTP GET request that initiates a sync connection. The value + /// specified here must match with the server's expectation. Changing + /// the value of url_prefix should be matched with a corresponding + /// change of the server side proxy. + std::string url_prefix = "/realm-sync"; + + /// authorization_header_name is the name of the HTTP header containing + /// the Realm access token. The value of the HTTP header is + /// "Realm-Access-Token version=1 token=....". + /// authorization_header_name does not participate in session + /// multiplexing partitioning. + std::string authorization_header_name = "Authorization"; + + /// custom_http_headers is a map of custom HTTP headers. The keys of the map + /// are HTTP header names, and the values are the corresponding HTTP + /// header values. + /// If "Authorization" is used as a custom header name, + /// authorization_header_name must be set to anther value. + std::map custom_http_headers; + + /// Sessions can be multiplexed over the same TCP/SSL connection. + /// Sessions might share connection if they have identical server_address, + /// server_port, and protocol. multiplex_ident is a parameter that allows + /// finer control over session multiplexing. If two sessions have distinct + /// multiplex_ident, they will never share connection. The typical use of + /// multilex_ident is to give sessions with incompatible SSL requirements + /// distinct multiplex_idents. + /// multiplex_ident can be any string and the value has no meaning except + /// for partitioning the sessions. + std::string multiplex_ident; + + /// Controls whether the server certificate is verified for SSL + /// connections. It should generally be true in production. + 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. + /// ssl_trust_certificate_path is only used if the protocol is ssl and + /// verify_servers_ssl_certificate is true. + /// + /// 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 server_address 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). + /// + /// If ssl_trust_certificate_path is None (default), ssl_verify_callback + /// (see below) is used if set, and the default device trust/anchor + /// store is used otherwise. + util::Optional ssl_trust_certificate_path; + + /// If Client::Config::ssl_verify_callback is set, that function is called + /// to verify the certificate, unless verify_servers_ssl_certificate is + /// false. + + /// ssl_verify_callback is used to implement custom SSL certificate + /// verification. it is only used if the protocol is SSL, + /// verify_servers_ssl_certificate is true and ssl_trust_certificate_path + /// is None. + /// + /// The signature of ssl_verify_callback is + /// + /// bool(const std::string& server_address, + /// port_type server_port, + /// const char* pem_data, + /// size_t pem_size, + /// int preverify_ok, + /// int depth); + /// + /// server address and server_port is the address and port of the server + /// that a SSL connection is being established to. They are identical to + /// the server_address and server_port set in this config file and are + /// passed for convenience. + /// pem_data is the certificate of length pem_size in + /// the PEM format. preverify_ok is OpenSSL's preverification of the + /// certificate. preverify_ok is either 0, or 1. If preverify_ok is 1, + /// OpenSSL has accepted the certificate and it will generally be safe + /// to trust that certificate. depth represents the position of the + /// certificate in the certificate chain sent by the server. depth = 0 + /// represents the actual server certificate that should contain the + /// host name(server address) of the server. The highest depth is the + /// root certificate. + /// The callback function will receive the certificates starting from + /// the root certificate and moving down the chain until it reaches the + /// server's own certificate with a host name. The depth of the last + /// certificate is 0. The depth of the first certificate is chain + /// length - 1. + /// + /// The return value of the callback function decides whether the + /// client accepts the certificate. If the return value is false, the + /// processing of the certificate chain is interrupted and the SSL + /// connection is rejected. If the return value is true, the verification + /// process continues. If the callback function returns true for all + /// presented certificates including the depth == 0 certificate, the + /// SSL connection is accepted. + /// + /// A recommended way of using the callback function is to return true + /// if preverify_ok = 1 and depth > 0, + /// always check the host name if depth = 0, + /// and use an independent verification step if preverify_ok = 0. + /// + /// Another possible way of using the callback is to collect all the + /// certificates until depth = 0, and present the entire chain for + /// independent verification. + std::function ssl_verify_callback; + + /// signed_user_token is a cryptographically signed token describing the + /// identity and access rights of the current user. + std::string signed_user_token; + + /// 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. Please see "Callback semantics" section under Client for + /// more on this. + /// + /// \sa make_client_replication(), TrivialChangesetCooker. + std::shared_ptr changeset_cooker; + + /// The encryption key the DB will be opened with. + util::Optional> encryption_key; + + /// ClientReset is used for both async open and client reset. If + /// client_reset is not util::none, the sync client will perform + /// async open for this session if the local Realm does not exist, and + /// client reset if the local Realm exists. If client_reset is + /// util::none, an ordinary sync session will take place. + /// + /// A session will perform async open by downloading a state Realm, and + /// some metadata, from the server, patching up the metadata part of + /// the Realm and finally move the downloaded Realm into the path of + /// the local Realm. After completion of async open, the application + /// can open and use the Realm. + /// + /// A session will perform client reset by downloading a state Realm, and + /// some metadata, from the server. After download, the state Realm will + /// be integrated into the local Realm in a write transaction. The + /// application is free to use the local realm during the entire client + /// reset. Like a DOWNLOAD message, the application will not be able + /// to perform a write transaction at the same time as the sync client + /// performs its own write transaction. Client reset is not more + /// disturbing for the application than any DOWNLOAD message. The + /// application can listen to change notifications from the client + /// reset exactly as in a DOWNLOAD message. + /// + /// The client reset will recover non-uploaded changes in the local + /// Realm if and only if 'recover_local_changes' is true. In case, + /// 'recover_local_changes' is false, the local Realm state will hence + /// be set to the server's state (server wins). + /// + /// Async open and client reset require a private directory for + /// metadata. This directory must be specified in the option + /// 'metadata_dir'. The metadata_dir must not be touched during async + /// open or client reset. The metadata_dir can safely be removed at + /// times where async open or client reset do not take place. The sync + /// client attempts to clean up metadata_dir. The metadata_dir can be + /// reused across app restarts to resume an interrupted download. It is + /// recommended to leave the metadata_dir unchanged except when it is + /// known that async open or client reset is done. + /// + /// The recommended usage of async open is to use it for the initial + /// bootstrap if Realm usage is not needed until after the server state + /// has been downloaded. + /// + /// The recommended usage of client reset is after a previous session + /// encountered an error that implies the need for a client reset. It + /// is not recommended to persist the need for a client reset. The + /// application should just attempt to synchronize in the usual fashion + /// and only after hitting an error, start a new session with a client + /// reset. In other words, if the application crashes during a client reset, + /// the application should attempt to perform ordinary synchronization + /// after restart and switch to client reset if needed. + /// + /// Error codes that imply the need for a client reset are the session + /// level error codes: + /// + /// 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) + /// + /// However, other errors such as bad changeset (UPLOAD) could also be resolved + /// with a client reset. Client reset can even be used without any prior error + /// if so desired. + /// + /// After completion of async open and client reset, the sync client + /// will continue synchronizing with the server in the usual fashion. + /// + /// The progress of async open and client reset can be tracked with the + /// standard progress handler. + /// + /// Async open and client reset are done when the progress handler + /// arguments satisfy "progress_version > 0". However, if the + /// application wants to ensure that it has all data present on the + /// server, it should wait for download completion using either + /// void async_wait_for_download_completion(WaitOperCompletionHandler) + /// or + /// bool wait_for_download_complete_or_client_stopped(). + /// + /// The option 'require_recent_state_realm' is used for async open to + /// request a recent state Realm. A recent state Realm is never empty + /// (unless there is no data), and is recent in the sense that it was + /// produced by the current incarnation of the server. Recent does not + /// mean the absolutely newest possible state Realm, since that might + /// lead to too excessive work on the server. Setting + /// 'require_recent_state_realm' to true might lead to more work + /// performed by the server but it ensures that more data is downloaded + /// using async open instead of ordinary synchronization. It is + /// recommended to set 'require_recent_state_realm' to true. Client + /// reset always downloads a recent state Realm. + struct ClientReset { + std::string metadata_dir; + bool recover_local_changes = true; + bool require_recent_state_realm = true; + }; + util::Optional client_reset_config; + + struct ProxyConfig { + enum class Type { HTTP, HTTPS } type; + std::string address; + port_type port; + }; + util::Optional proxy_config; + + /// Set to true to disable the upload process for this session. This + /// includes the sending of empty UPLOAD messages. + /// + /// This feature exists exclusively for testing purposes at this time. + bool disable_upload = false; + + /// Set to true to disable sending of empty UPLOAD messages for this + /// session. + /// + /// This feature exists exclusively for testing purposes at this time. + bool disable_empty_upload = false; + + /// Set to true to cause the integration of the first received changeset + /// (in a DOWNLOAD message) to fail. + /// + /// This feature exists exclusively for testing purposes at this time. + bool simulate_integration_error = false; + }; + + /// \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 = {}); + + /// This leaves the right-hand side session object detached. See "Thread + /// safety" section under detach(). + Session(Session&&) noexcept; + + /// Create a detached session object (see detach()). + Session() noexcept; + + /// Implies detachment. See "Thread safety" section under detach(). + ~Session() noexcept; + + /// Detach the object on the left-hand side, then "steal" the session from + /// the object on the right-hand side, if there is one. This leaves the + /// object on the right-hand side detached. See "Thread safety" section + /// under detach(). + Session& operator=(Session&&) noexcept; + + /// Detach this sesion object from the client object (Client). If the + /// session object is already detached, this function has no effect + /// (idempotency). + /// + /// Detachment initiates session termination, which is an event that takes + /// place shortly therafter in the context of the client's event loop + /// thread. + /// + /// A detached session object may be destroyed, move-assigned to, and moved + /// from. Apart from that, it is an error to call any function other than + /// detach() on a detached session object. + /// + /// Thread safety: Detachment is not a thread-safe operation. This means + /// that detach() may not be executed by two threads concurrently, and may + /// not execute concurrently with object destruction. Additionally, + /// detachment must not execute concurrently with a moving operation + /// involving the session object on the left or right-hand side. See move + /// constructor and assigment operator. + void detach() 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 get called before the call + /// to bind() returns, and it may get called (or continue to execute) after + /// the session object is destroyed. Please see "Callback semantics" section + /// under Session for more on this. + 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 equal to downloaded_bytes plus an estimate of + /// the size of the remaining server history. + /// + /// 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. downloadable_bytes can + /// decrease for two reasons: server side compaction and changesets of + /// local origin. Code using downloadable_bytes must not assume that it + /// is increasing. + /// + /// 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 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. + /// + /// CAUTION: The specified callback function may get called before the call + /// to bind() returns, and it may get called (or continue to execute) after + /// the session object is destroyed. Please see "Callback semantics" section + /// under Session for more on this. + void set_progress_handler(std::function); + + enum class ConnectionState { disconnected, connecting, connected }; + + /// \brief Information about an error causing a session to be temporarily + /// disconnected from the server. + /// + /// In general, the connection will be automatically reestablished + /// later. Whether this happens quickly, generally depends on \ref + /// is_fatal. If \ref is_fatal is true, it means that the error is deemed to + /// be of a kind that is likely to persist, and cause all future reconnect + /// attempts to fail. In that case, if another attempt is made at + /// reconnecting, the delay will be substantial (at least an hour). + /// + /// \ref error_code specifies the error that caused the connection to be + /// closed. For the list of errors reported by the server, see \ref + /// ProtocolError (or `protocol.md`). For the list of errors corresponding + /// to protocol violations that are detected by the client, see + /// Client::Error. The error may also be a system level error, or an error + /// from one of the potential intermediate protocol layers (SSL or + /// WebSocket). + /// + /// \ref detailed_message is the most detailed message available to describe + /// the error. It is generally equal to `error_code.message()`, but may also + /// be a more specific message (one that provides extra context). The + /// purpose of this message is mostly to aid in debugging. For non-debugging + /// purposes, `error_code.message()` should generally be considered + /// sufficient. + /// + /// \sa set_connection_state_change_listener(). + struct ErrorInfo { + std::error_code error_code; + bool is_fatal; + const std::string& detailed_message; + }; + + using ConnectionStateChangeListener = void(ConnectionState, const ErrorInfo*); + + /// \brief Install a connection state change listener. + /// + /// Sets a function to be called whenever the state of the underlying + /// network connection changes between "disconnected", "connecting", and + /// "connected". The initial state is always "disconnected". The next state + /// after "disconnected" is always "connecting". The next state after + /// "connecting" is either "connected" or "disconnected". The next state + /// after "connected" is always "disconnected". A switch to the + /// "disconnected" state only happens when an error occurs. + /// + /// Whenever the installed function is called, an ErrorInfo object is passed + /// when, and only when the passed state is ConnectionState::disconnected. + /// + /// When multiple sessions share a single connection, the state changes will + /// be reported for each session in turn. + /// + /// 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 get called before the call + /// to bind() returns, and it may get called (or continue to execute) after + /// the session object is destroyed. Please see "Callback semantics" section + /// under Session for more on this. + void set_connection_state_change_listener(std::function); + + //@{ + /// Deprecated! Use set_connection_state_change_listener() instead. + using ErrorHandler = void(std::error_code, bool is_fatal, const std::string& detailed_message); + 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. + /// + /// bind() binds this session to the specified server side Realm using the + /// parameters specified in the Session::Config object. + /// + /// The two other forms of bind() are convenience functions. + /// void bind(std::string server_address, std::string server_path, + /// std::string signed_user_token, port_type server_port = 0, + /// ProtocolEnvelope protocol = ProtocolEnvelope::realm); + /// replaces the corresponding parameters from the Session::Config object + /// before the session is bound. + /// void bind(std::string server_url, std::string signed_user_token) parses + /// the \param server_url and replaces the parameters in the Session::Config object + /// before the session is bound. + /// + /// \param server_url For example "realm://sync.realm.io/test". See + /// server_address, server_path, and server_port in Session::Config for + /// information about the individual components of the URL. See + /// ProtocolEnvelope for the list of available URL schemes and the + /// associated default ports. + /// + /// \throw BadServerUrl if the specified server URL is malformed. + void bind(); + 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, + ProtocolEnvelope protocol = ProtocolEnvelope::realm); + /// @} + + /// \brief Refresh the access token associated with this session. + /// + /// This causes the REFRESH protocol message to be sent to the server. See + /// ProtocolEnvelope. It is an error to pass a token with a different user + /// identity than the token used to initiate the session. + /// + /// In an on-going session the application may expect the 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, and seamless connectivity to the server. + /// + /// If the application does not proactively refresh an expiring token, the + /// session will eventually be disconnected. The application can detect this + /// by monitoring the connection state + /// (set_connection_state_change_listener()), and check whether the error + /// code is `ProtocolError::token_expired`. Such a session can then be + /// revived by calling refresh() with a newly acquired access token. + /// + /// Due to protocol techicalities, a race condition exists that can cause a + /// session to become, and remain disconnected after a new access token has + /// been passed to refresh(). The application can work around this race + /// condition by detecting the `ProtocolError::token_expired` error, and + /// always initiate a token renewal in this case. + /// + /// 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 ProtocolEnvelope. + 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 behalf, 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 implicitly + /// announced. Also all preexisting changesets are implicitly 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 another wait operation is in + /// progress by another thread, the waiting period of first operation may, + /// or may not get extended. The application must not assume either. The + /// application 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 get called before the + /// call to the waiting function returns, and it may get called (or continue + /// to execute) after the session object is destroyed. Please see "Callback + /// semantics" section under Session for more on this. + /// + /// 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 of + /// 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's event loop thread exits from Client::run(), whichever happens + /// first. + /// + /// It is an error to call these functions before bind() has been called, + /// and has returned. + /// + /// CAUTION: If Client::run() returns while a wait operation is in progress, + /// these waiting functions return immediately, even if the completion + /// condition is not yet satisfied. The completion condition is guaranteed + /// to be satisfied only when these functions return true. + /// + /// \return True only if the completion condition was satisfied. False if + /// the client's event loop thread exited from Client::run() in which case + /// the completion condition may, or may not have been satisfied. + /// + /// Note: These functions are fully thread-safe. That is, they may be called + /// by any thread, and by multiple threads concurrently. + bool wait_for_upload_complete_or_client_stopped(); + bool 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(); + + /// \brief Change address of server for this session. + void override_server(std::string address, port_type); + + /// \brief Initiate a serialized transaction. + /// + /// Asynchronously waits for completion of any serialized transactions, that + /// are already in progress via the same session object, then waits for + /// the download process to complete (async_wait_for_download_completion()), + /// then pauses the upload process. The upload process will be resumed when + /// async_try_complete_serial_transact() or abort_serial_transact() is + /// called. + /// + /// Changesets produced by local transactions, that are committed after the + /// completion of the initiation of a serialized transaction, are guaranteed + /// to not be uploaded until after (or during) the completion of that + /// serialized transaction (async_try_complete_serial_transact()). + /// + /// If the initiation of a serialized transaction is successfully completed, + /// that is, if the specified handler gets called with an std::error_code + /// argument that evaluates to false in a boolean context, then the + /// application is required to eventually call + /// async_try_complete_serial_transact() to complete the transaction, or + /// abort_serial_transact() to abort it. If + /// async_try_complete_serial_transact() fails (throws), the application is + /// required to follow up with a call to abort_serial_transact(). + /// + /// If the session object is destroyed before initiation process completes, + /// the specified handler will be called with error + /// `util::error::operation_aborted`. Currently, this is the only possible + /// error that can be reported through this handler. + /// + /// This feature is only available when the server supports version 28, or + /// later, of the synchronization protocol. See + /// get_current_protocol_version(). + /// + /// This feature is not currently supported with Partial Synchronization, + /// and in a server cluster, it is currently only supported on the root + /// node. + void async_initiate_serial_transact(SerialTransactInitiationHandler); + + /// \brief Complete a serialized transaction. + /// + /// Initiate the completion of the serialized transaction. This involves + /// sending the specified changeset to the server, and waiting for the + /// servers response. + /// + /// If the session object is destroyed before completion process completes, + /// the specified handler will be called with error + /// `util::error::operation_aborted`. + /// + /// Otherwise, if the server does not support serialized transactions, the + /// specified handler will be called with error + /// `util::MiscExtErrors::operation_not_supported`. This happens if the + /// negotiated protocol version is too old, if serialized transactions are + /// disallowed by the server, or if it is not allowed for the Realm file in + /// question (partial synchronization). + /// + /// Otherwise, the specified handler will be called with an error code + /// argument that evaluates to false in a boolean context, and the + /// `accepted` argument will be true if, and only if the transaction was + /// accepted by the server. + /// + /// \param upload_anchor The upload cursor associated with the snapshot on + /// which the specified changeset is based. Use + /// sync::ClientHistory::get_upload_anchor_of_current_transact() to obtain + /// it. Note that + /// sync::ClientHistory::get_upload_anchor_of_current_transact() needs to be + /// called during the transaction that is used to produce the changeset of + /// the serialized transaction. + /// + /// \param changeset A changeset obtained from an aborted transaction on the + /// Realm file associated with this session. Use + /// sync::ClientHistory::get_sync_changeset() to obtain it. The transaction, + /// which is used to produce teh changeset, needs to be rolled back rather + /// than committed, because the decision of whether to accept the changes + /// need to be delegated to the server. Note that + /// sync::ClientHistory::get_sync_Changeset_of_current_transact() needs to + /// be called at the end of the transaction, that is used to produce the + /// changeset, but before the rollback operation. + void async_try_complete_serial_transact(UploadCursor upload_anchor, + SerialTransactChangeset changeset, + SerialTransactCompletionHandler); + + /// \brief Abort a serialized transaction. + /// + /// Must be called if async_try_complete_serial_transact() fails, i.e., if + /// it throws, or if async_try_complete_serial_transact() is not called at + /// all. Must not be called if async_try_complete_serial_transact() + /// succeeds, i.e., if it does not throw. + /// + /// Will resume upload process. + void abort_serial_transact() noexcept; + +private: + class Impl; + Impl* m_impl = nullptr; + + void abandon() noexcept; + 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 connection state change listeners 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_client_file_ident = 106, ///< Bad client file identifier (IDENT) + 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) + ssl_server_cert_rejected = 117, ///< SSL server certificate rejected + pong_timeout = 118, ///< Timeout on reception of PONG respone message + bad_client_file_ident_salt = 119, ///< Bad client file identifier salt (IDENT) + bad_file_ident = 120, ///< Bad file identifier (ALLOC) + connect_timeout = 121, ///< Sync connection was not fully established in time + bad_timestamp = 122, ///< Bad timestamp (PONG) + bad_protocol_from_server = 123, ///< Bad or missing protocol version information from server + client_too_old_for_server = 124, ///< Protocol version negotiation failed: Client is too old for server + client_too_new_for_server = 125, ///< Protocol version negotiation failed: Client is too new for server + protocol_mismatch = 126, ///< Protocol version negotiation failed: No version supported by both client and server + bad_state_message = 127, ///< Bad values in state message (STATE) + missing_protocol_feature = 128, ///< Requested feature missing in negotiated protocol version + bad_serial_transact_status = 129, ///< Bad status of serialized transaction (TRANSACT) + bad_object_id_substitutions = 130, ///< Bad encoded object identifier substitutions (TRANSACT) + http_tunnel_failed = 131, ///< Failed to establish HTTP tunnel with configured proxy +}; + +const std::error_category& client_error_category() noexcept; + +std::error_code make_error_code(Client::Error) noexcept; + +std::ostream& operator<<(std::ostream& os, Session::Config::ProxyConfig::Type); + +} // 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 Session::Session(Session&& sess) noexcept: + m_impl{sess.m_impl} +{ + sess.m_impl = nullptr; +} + +inline Session::Session() noexcept +{ +} + +inline Session::~Session() noexcept +{ + if (m_impl) + abandon(); +} + +inline Session& Session::operator=(Session&& sess) noexcept +{ + if (m_impl) + abandon(); + m_impl = sess.m_impl; + sess.m_impl = nullptr; + return *this; +} + +inline void Session::detach() noexcept +{ + if (m_impl) + abandon(); + m_impl = nullptr; +} + +inline void Session::set_error_handler(std::function handler) +{ + auto handler_2 = [handler=std::move(handler)](ConnectionState state, + const ErrorInfo* error_info) { + if (state != ConnectionState::disconnected) + return; + REALM_ASSERT(error_info); + std::error_code ec = error_info->error_code; + bool is_fatal = error_info->is_fatal; + const std::string& detailed_message = error_info->detailed_message; + handler(ec, is_fatal, detailed_message); // Throws + }; + set_connection_state_change_listener(std::move(handler_2)); // Throws +} + +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/src/vendor-include/realm-ios/include/realm/sync/crypto_server.hpp b/src/vendor-include/realm-ios/include/realm/sync/crypto_server.hpp new file mode 100644 index 000000000..c241a7117 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/sync/crypto_server.hpp @@ -0,0 +1,91 @@ +/************************************************************************* + * + * 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 key from a PEM buffer + static PKey load_public(BinaryData pem_buffer); + + /// Load RSA public/private keypair from \a pemfile. + static PKey load_private(const std::string& pemfile); + /// Load RSA public/private keypair from a PEM buffer + static PKey load_private(BinaryData pem_buffer); + + /// 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/src/vendor-include/realm-ios/include/realm/sync/feature_token.hpp b/src/vendor-include/realm-ios/include/realm/sync/feature_token.hpp new file mode 100644 index 000000000..86db015a4 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/sync/feature_token.hpp @@ -0,0 +1,69 @@ +/************************************************************************* + * + * 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_FEATURE_TOKEN_HPP +#define REALM_SYNC_FEATURE_TOKEN_HPP + +#include + +#if !REALM_MOBILE && !defined(REALM_EXCLUDE_FEATURE_TOKENS) +#define REALM_HAVE_FEATURE_TOKENS 1 +#else +#define REALM_HAVE_FEATURE_TOKENS 0 +#endif + +#if REALM_HAVE_FEATURE_TOKENS + +#include + +#include + +namespace realm { +namespace sync { + +class FeatureGate { +public: + + // The constructor takes a JWT token as argument. + // The constructor throws a std::runtime_error if + // the token is invalid. An invalid token is a token + // that has bad syntax, is not signed by Realm, or is + // expired. + FeatureGate(StringData token); + + // Constructs a feature gate without any features. + FeatureGate(); + ~FeatureGate(); + + FeatureGate(FeatureGate&&); + FeatureGate& operator=(FeatureGate&&); + + bool has_feature(StringData feature_name); + +private: + struct Impl; + std::unique_ptr m_impl; +}; + + +} // namespace sync +} // namespace realm + +#endif // REALM_HAVE_FEATURE_TOKENS +#endif // REALM_SYNC_FEATURE_TOKEN_HPP diff --git a/src/vendor-include/realm-ios/include/realm/sync/fingerprint.hpp b/src/vendor-include/realm-ios/include/realm/sync/fingerprint.hpp new file mode 100644 index 000000000..13b54ab35 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/sync/fingerprint.hpp @@ -0,0 +1,53 @@ +/************************************************************************* + * + * REALM CONFIDENTIAL + * __________________ + * + * [2011] - [2018] 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_ENCRYPT_FINGERPRINT_HPP +#define REALM_ENCRYPT_FINGERPRINT_HPP + +#include +#include + +#include + +namespace realm { +namespace encrypt { + +// calculate_fingerprint() calculates, and returns, a fingerprint of an +// encryption key. The input key can be util::none in order to calculate a +// fingerprint even in the case of unencrypted Realms. +// +// An intruder cannot recover an unknown encryption_key from the fingerprint, +// and it is safe to save the fingerprint in a file together with the encrypted +// Realms. +// +// calculate_fingerprint() can be considered opaque, but currently the +// fingerprint is a colon separated hex representation of the SHA-256 hash of +// the encryption key. +std::string calculate_fingerprint(const util::Optional> encryption_key); + +// verify_fingerprint() returns true if `fingerprint` was obtained previously +// from calculate_fingerprint() with `encryption_key` as argument. Otherwise, +// verify_fingerprint() returns false with extremely high probability. +bool verify_fingerprint(const std::string& fingerprint, + const util::Optional> encryption_key); + +} // namespace encrypt +} // namespace realm + +#endif // REALM_ENCRYPT_FINGERPRINT_HPP diff --git a/src/vendor-include/realm-ios/include/realm/sync/history.hpp b/src/vendor-include/realm-ios/include/realm/sync/history.hpp new file mode 100644 index 000000000..d2f9f1141 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/sync/history.hpp @@ -0,0 +1,608 @@ +/************************************************************************* + * + * 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 + +#include +#include +#include +#include +#include +#include +#include + +#ifndef REALM_SYNC_HISTORY_HPP +#define REALM_SYNC_HISTORY_HPP + + +namespace realm { +namespace _impl { + +struct ObjectIDHistoryState; + +} // namespace _impl +} // namespace realm + + +namespace realm { +namespace sync { + +struct VersionInfo { + /// Realm snapshot version. + version_type realm_version = 0; + + /// The synchronization version corresponding to `realm_version`. + /// + /// In the context of the client-side history type `sync_version.version` + /// will currently always be equal to `realm_version` and + /// `sync_version.salt` will always be zero. + SaltedVersion sync_version = {0, 0}; +}; + +struct SerialTransactSubstitutions { + struct Class { + InternString name; + std::size_t substitutions_end; + }; + std::vector classes; + std::vector> substitutions; +}; + +timestamp_type generate_changeset_timestamp() noexcept; + +// FIXME: in C++17, switch to using std::timespec in place of last two +// arguments. +void map_changeset_timestamp(timestamp_type, std::time_t& seconds_since_epoch, + long& nanoseconds) noexcept; + +/// Thrown if changeset cooking is not either consistently on or consistently +/// off during synchronization (ClientHistory::set_sync_progress() and +/// ClientHistory::integrate_server_changesets()). +class InconsistentUseOfCookedHistory; + +/// Thrown if a bad server version is passed to +/// ClientHistory::get_cooked_status(). +class BadCookedServerVersion; + + +class ClientReplicationBase : + public SyncReplication { +public: + using SyncTransactCallback = void(VersionID old_version, VersionID new_version); + + /// Get the version of the latest snapshot of the associated Realm, as well + /// as the client file identifier and the synchronization progress as they + /// are stored in that snapshot. + /// + /// The returned current client version is the version produced by the last + /// changeset in the history. The type of version returned here, is the one + /// that identifies an entry in the sync history. Whether this is the same + /// as the snapshot number of the Realm file depends on the history + /// implementation. + /// + /// The returned client file identifier is the one that was last stored by + /// set_client_file_ident(), or `SaltedFileIdent{0, 0}` if + /// set_client_file_ident() has never been called. + /// + /// The returned SyncProgress is the one that was last stored by + /// set_sync_progress(), or `SyncProgress{}` if set_sync_progress() has + /// never been called. + virtual void get_status(version_type& current_client_version, + SaltedFileIdent& client_file_ident, + SyncProgress& progress) const = 0; + + /// Stores the server assigned client file identifier 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 more + /// than once per Realm file. + /// + /// \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. + /// + /// \param fix_up_object_ids The object ids that depend on client file ident + /// will be fixed in both state and history if this parameter is true. If + /// it is known that there are no objects to fix, it can be set to false to + /// achieve higher performance. + /// + /// The client is required to obtain the file identifier before engaging in + /// synchronization proper, and it must store the identifier and use it to + /// reestablish the connection between the client file and the server file + /// when engaging in future synchronization sessions. + virtual void set_client_file_ident(SaltedFileIdent client_file_ident, + bool fix_up_object_ids) = 0; + + /// Stores the synchronization 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 \a progress. + /// + /// \param downloadable_bytes If specified, and if the implementation cares + /// about byte-level progress, this function updates the persistent record + /// of the estimate of the number of remaining bytes to be downloaded. + /// + /// \throw InconsistentUseOfCookedHistory If a changeset cooker has been + /// attached to this history object, and the Realm file does not have a + /// cooked history, and a cooked history can no longer be added because some + /// synchronization has already happened. Or if no changeset cooker has been + /// attached, and the Realm file does have a cooked history. + virtual void set_sync_progress(const SyncProgress& progress, + const std::uint_fast64_t* downloadable_bytes, VersionInfo&) = 0; + + struct UploadChangeset { + timestamp_type origin_timestamp; + file_ident_type origin_file_ident; + UploadCursor progress; + ChunkedBinaryData changeset; + std::unique_ptr buffer; + }; + + /// \brief Scan through the history for changesets to be uploaded. + /// + /// This function scans the history for changesets to be uploaded, i.e., for + /// changesets that are not empty, and were not produced by integration of + /// changesets recieved from the server. The scan begins at the position + /// specified by the initial value of \a upload_progress.client_version, and + /// ends no later than at the position specified by \a end_version. + /// + /// The implementation is allowed to end the scan before \a end_version, + /// such as to limit the combined size of returned changesets. However, if + /// the specified range contains any changesets that are supposed to be + /// uploaded, this function must return at least one. + /// + /// Upon return, \a upload_progress will have been updated to point to the + /// position from which the next scan should resume. This must be a position + /// after the last returned changeset, and before any remaining changesets + /// that are supposed to be uploaded, although never a position that + /// succeeds \a end_version. + /// + /// The value passed as \a upload_progress by the caller, must either be one + /// that was produced by an earlier invocation of + /// find_uploadable_changesets(), one that was returned by get_status(), or + /// one that was received by the client in a DOWNLOAD message from the + /// server. When the value comes from a DOWNLOAD message, it is supposed to + /// reflect a value of UploadChangeset::progress produced by an earlier + /// invocation of find_uploadable_changesets(). + /// + /// Found changesets are added to \a uploadable_changesets. + /// + /// \param locked_server_version will be set to the value that should be + /// used as `` in a DOWNLOAD message. + /// + /// For changesets of local origin, UploadChangeset::origin_file_ident will + /// be zero. + virtual void find_uploadable_changesets(UploadCursor& upload_progress, version_type end_version, + std::vector& uploadable_changesets, + version_type& locked_server_version) const = 0; + + using RemoteChangeset = Transformer::RemoteChangeset; + + // FIXME: Apparently, this feature is expected by object store, but why? + // What is it ultimately used for? (@tgoyne) + class SyncTransactReporter { + public: + virtual void report_sync_transact(VersionID old_version, VersionID new_version) = 0; + protected: + ~SyncTransactReporter() {} + }; + + enum class IntegrationError { + bad_origin_file_ident, + bad_changeset + }; + + /// \brief Integrate a sequence of changesets received from the server 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). + /// + /// This function checks whether the specified changesets specify valid + /// remote origin file identifiers and whether the changesets contain valid + /// sequences of instructions. The caller must already have ensured that the + /// origin file identifiers are strictly positive and not equal to the file + /// identifier assigned to this client by the server. + /// + /// If any of the changesets are invalid, this function returns false and + /// sets `integration_error` to the appropriate value. If they are all + /// deemed valid, this function updates \a version_info to reflect the new + /// version produced by the transaction. + /// + /// \param progress The synchronization progress is what was received in the + /// DOWNLOAD message along with the specified changesets. The progress will + /// be persisted along with the changesets. + /// + /// \param downloadable_bytes If specified, and if the implementation cares + /// about byte-level progress, this function updates the persistent record + /// of the estimate of the number of remaining bytes to be downloaded. + /// + /// \param num_changesets The number of passed changesets. Must be non-zero. + /// + /// \param transact_reporter An optional callback which will be called with the + /// version immediately processing the sync transaction and that of the sync + /// transaction. + /// + /// \throw InconsistentUseOfCookedHistory If a changeset cooker has been + /// attached to this history object, and the Realm file does not have a + /// cooked history, and a cooked history can no longer be added because some + /// synchronization has already happened. Or if no changeset cooker has been + /// attached, and the Realm file does have a cooked history. + virtual bool integrate_server_changesets(const SyncProgress& progress, + const std::uint_fast64_t* downloadable_bytes, + const RemoteChangeset* changesets, + std::size_t num_changesets, VersionInfo& new_version, + IntegrationError& integration_error, util::Logger&, + SyncTransactReporter* transact_reporter = nullptr, + const SerialTransactSubstitutions* = nullptr) = 0; + +protected: + ClientReplicationBase(const std::string& realm_path); +}; + + + +class ClientReplication : public ClientReplicationBase { +public: + class ChangesetCooker; + class Config; + + /// Get the persisted upload/download progress in bytes. + virtual void get_upload_download_bytes(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& snapshot_version) = 0; + + /// See set_cooked_progress(). + struct CookedProgress { + std::int_fast64_t changeset_index = 0; + std::int_fast64_t intrachangeset_progress = 0; + }; + + /// Get information about the current state of the cooked history including + /// the point of progress of its consumption. + /// + /// \param server_version The server version associated with the last cooked + /// changeset that should be skipped. See `/doc/cooked_history.md` for an + /// explanation of the rationale behind this. Specifying zero means that no + /// changesets should be skipped. It is an error to specify a nonzero server + /// version that is not the server version associated with any of of the + /// cooked changesets, or to specify a nonzero server version that precedes + /// the one, that is associated with the last cooked changeset that was + /// marked as consumed. Doing so, will cause BadCookedServerVersion to be + /// thrown. + /// + /// \param num_changesets Set to the total number of produced cooked + /// changesets over the lifetime of the Realm file to which this history + /// accessor object is attached. This is the number of previously consumed + /// changesets plus the number of unconsumed changesets remaining in the + /// Realm file. + /// + /// \param progress The point of progress of the consumption of the cooked + /// history. Initially, and until explicitly modified by + /// set_cooked_progress(), both `CookedProgress::changeset_index` and + /// `CookedProgress::intrachangeset_progress` are zero. If a nonzero value + /// was passed for \a server_version, \a progress will be transparently + /// adjusted to account for the skipped changesets. See also \a + /// num_skipped_changesets. If one or more changesets are skipped, + /// `CookedProgress::intrachangeset_progress` will be set to zero. + /// + /// \param num_skipped_changesets The number of skipped changesets. See also + /// \a server_version. + /// + /// \throw BadCookedServerVersion See \a server_version. + virtual void get_cooked_status(version_type server_version, std::int_fast64_t& num_changesets, + CookedProgress& progress, std::int_fast64_t& num_skipped_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 total 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. + /// + /// \param server_version Will be set to the version produced on the server + /// by an earlier form of the retreived changeset. If the cooked changeset + /// was produced (as output of cooker) before migration of the client-side + /// history compartment to schema version 2, then \a server_version will be + /// set to zero instead, because the real value is unkown. Zero is not a + /// possible value in any other case. + virtual void get_cooked_changeset(std::int_fast64_t index, util::AppendBuffer&, + version_type& server_version) const = 0; + + /// Persistently stores the point of progress of the consumer of cooked + /// changesets. + /// + /// The changeset index (CookedProgress::changeset_index) is the index (as + /// passed to get_cooked_changeset()) of the first unconsumed cooked + /// changset. Changesets at lower indexes will no longer be available. + /// + /// The intrachangeset progress field + /// (CookedProgress::intrachangeset_progress) will be faithfully persisted, + /// but will otherwise be treated as an opaque object by the history + /// internals. + /// + /// 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 (i.e., removal + /// of all changesets at indexes lower than + /// CookedProgress::intrachangeset_progress). Indeed, if this function is + /// never called, but cooked changesets are continually being produced, then + /// the Realm file will grow without bounds. + /// + /// It is an error if the specified index (CookedProgress::changeset_index) + /// is lower than the index returned by get_cooked_progress(), and if it is + /// higher that the value returned by get_num_cooked_changesets(). + /// + /// \return The snapshot number produced by the transaction performed + /// internally in set_cooked_progress(). This is also the client-side sync + /// version, and it should be passed to + /// sync::Session::nonsync_transact_notify() if a synchronization session is + /// in progress for the same file while set_cooked_progress() is + /// called. Doing so, ensures that the server will be notified about the + /// released server versions as soon as possible. + /// + /// \throw InconsistentUseOfCookedHistory If this file does not have a + /// cooked history and one can no longer be added because changesets of + /// remote origin has already been integrated. + virtual version_type set_cooked_progress(CookedProgress) = 0; + + /// \brief Get the number of cooked changesets so far produced for this + /// Realm. + /// + /// This is the same thing as is returned via \a num_changesets by + /// get_cooked_status(). + std::int_fast64_t get_num_cooked_changesets() const noexcept; + + /// \brief Returns the persisted progress that was last stored by + /// set_cooked_progress(). + /// + /// This is the same thing as is returned via \a progress by + /// get_cooked_status() when invoked with a server version of zero. + CookedProgress get_cooked_progress() const noexcept; + + /// Same as get_cooked_changeset(std::int_fast64_t, + /// util::AppendBuffer&, version_type&) but does not retreived the + /// server version. + void get_cooked_changeset(std::int_fast64_t index, util::AppendBuffer&) const; + + /// Return an upload cursor as it would be when the uploading process + /// reaches the snapshot to which the current transaction is bound. + /// + /// **CAUTION:** Must be called only while a transaction (read or write) is + /// in progress via the SharedGroup object associated with this history + /// object. + virtual UploadCursor get_upload_anchor_of_current_transact(const Transaction&) const = 0; + + /// Return the synchronization changeset of the current transaction as it + /// would be if that transaction was committed at this time. + /// + /// The returned memory reference may be invalidated by subsequent + /// operations on the Realm state. + /// + /// **CAUTION:** Must be called only while a write transaction is in + /// progress via the SharedGroup object associated with this history object. + virtual util::StringView get_sync_changeset_of_current_transact(const Transaction&) const noexcept = 0; + +protected: + ClientReplication(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 ClientReplication::ChangesetCooker { +public: + virtual ~ChangesetCooker() {} + + /// \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 ClientReplication::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::DB objects. +std::unique_ptr make_client_replication(const std::string& realm_path, + ClientReplication::Config = {}); + + + +// Implementation + +inline ClientReplicationBase::ClientReplicationBase(const std::string& realm_path): + SyncReplication{realm_path} // Throws +{ +} + +inline timestamp_type generate_changeset_timestamp() noexcept +{ + namespace chrono = std::chrono; + // Unfortunately, C++11 does not specify what the epoch is for + // `chrono::system_clock` (or for any other clock). It is believed, however, + // that there is a de-facto standard, that the Epoch for + // `chrono::system_clock` is the Unix epoch, i.e., 1970-01-01T00:00:00Z. See + // http://stackoverflow.com/a/29800557/1698548. Additionally, it is assumed + // that leap seconds are not included in the value returned by + // time_since_epoch(), i.e., that it conforms to POSIX time. This is known + // to be true on Linux. + // + // FIXME: Investigate under which conditions OS X agrees with POSIX about + // not including leap seconds in the value returned by time_since_epoch(). + // + // FIXME: Investigate whether Microsoft Windows agrees with POSIX about + // about not including leap seconds in the value returned by + // time_since_epoch(). + auto time_since_epoch = chrono::system_clock::now().time_since_epoch(); + std::uint_fast64_t millis_since_epoch = + chrono::duration_cast(time_since_epoch).count(); + // `offset_in_millis` is the number of milliseconds between + // 1970-01-01T00:00:00Z and 2015-01-01T00:00:00Z not counting leap seconds. + std::uint_fast64_t offset_in_millis = 1420070400000ULL; + return timestamp_type(millis_since_epoch - offset_in_millis); +} + +inline void map_changeset_timestamp(timestamp_type timestamp, std::time_t& seconds_since_epoch, + long& nanoseconds) noexcept +{ + std::uint_fast64_t offset_in_millis = 1420070400000ULL; + std::uint_fast64_t millis_since_epoch = std::uint_fast64_t(offset_in_millis + timestamp); + seconds_since_epoch = std::time_t(millis_since_epoch / 1000); + nanoseconds = long(millis_since_epoch % 1000 * 1000000L); +} + +class InconsistentUseOfCookedHistory : public std::exception { +public: + InconsistentUseOfCookedHistory(const char* message) noexcept + : m_message{message} + { + } + const char* what() const noexcept override final + { + return m_message; + } + +private: + const char* m_message; +}; + +class BadCookedServerVersion : public std::exception { +public: + BadCookedServerVersion(const char* message) noexcept + : m_message{message} + { + } + const char* what() const noexcept override final + { + return m_message; + } +private: + const char* m_message; +}; + +inline ClientReplication::ClientReplication(const std::string& realm_path): + ClientReplicationBase{realm_path} // Throws +{ +} + +inline std::int_fast64_t ClientReplication::get_num_cooked_changesets() const noexcept +{ + version_type server_version = 0; // Skip nothing + std::int_fast64_t num_changesets = 0; + ClientReplication::CookedProgress progress; + std::int_fast64_t num_skipped_changesets = 0; + get_cooked_status(server_version, num_changesets, progress, num_skipped_changesets); + REALM_ASSERT(progress.changeset_index <= num_changesets); + REALM_ASSERT(num_skipped_changesets == 0); + return num_changesets; +} + +inline auto ClientReplication::get_cooked_progress() const noexcept -> CookedProgress +{ + version_type server_version = 0; // Skip nothing + std::int_fast64_t num_changesets = 0; + ClientReplication::CookedProgress progress; + std::int_fast64_t num_skipped_changesets = 0; + get_cooked_status(server_version, num_changesets, progress, num_skipped_changesets); + REALM_ASSERT(progress.changeset_index <= num_changesets); + REALM_ASSERT(num_skipped_changesets == 0); + return progress; +} + +inline void ClientReplication::get_cooked_changeset(std::int_fast64_t index, util::AppendBuffer& buffer) const +{ + version_type server_version; // Dummy + get_cooked_changeset(index, buffer, server_version); // Throws +} + +} // namespace sync +} // namespace realm + +#endif // REALM_SYNC_HISTORY_HPP diff --git a/src/vendor-include/realm-ios/include/realm/sync/instruction_applier.hpp b/src/vendor-include/realm-ios/include/realm/sync/instruction_applier.hpp new file mode 100644 index 000000000..a1367e785 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/sync/instruction_applier.hpp @@ -0,0 +1,146 @@ +/************************************************************************* + * + * 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_SYNC_IMPL_INSTRUCTION_APPLIER_HPP +#define REALM_SYNC_IMPL_INSTRUCTION_APPLIER_HPP + +#include +#include +#include +#include +#include + + +namespace realm { +namespace sync { + +struct Changeset; + +struct InstructionApplier { + explicit InstructionApplier(Transaction&, TableInfoCache&) noexcept; + + /// Throws BadChangesetError if application fails due to a problem with the + /// changeset. + /// + /// FIXME: Consider using std::error_code instead of throwing + /// BadChangesetError. + void apply(const Changeset&, util::Logger*); + + void begin_apply(const Changeset&, util::Logger*) noexcept; + void end_apply() noexcept; + +protected: + StringData get_string(InternString) const; + StringData get_string(StringBufferRange) const; +#define REALM_DECLARE_INSTRUCTION_HANDLER(X) void operator()(const Instruction::X&); + REALM_FOR_EACH_INSTRUCTION_TYPE(REALM_DECLARE_INSTRUCTION_HANDLER) +#undef REALM_DECLARE_INSTRUCTION_HANDLER + friend struct Instruction; // to allow visitor + + template static void apply(A& applier, const Changeset&, util::Logger*); + + // Allows for in-place modification of changeset while applying it + template static void apply(A& applier, Changeset&, util::Logger*); + + TableRef table_for_class_name(StringData) const; // Throws + REALM_NORETURN void bad_transaction_log(const char*) const; + + Transaction& m_transaction; + TableInfoCache& m_table_info_cache; + std::unique_ptr m_selected_array; + TableRef m_selected_table; + TableRef m_link_target_table; + + template + void log(const char* fmt, Args&&... args) + { + if (m_logger) { + m_logger->trace(fmt, std::forward(args)...); // Throws + } + } + +private: + const Changeset* m_log = nullptr; + util::Logger* m_logger = nullptr; +}; + + + + +// Implementation + +inline InstructionApplier::InstructionApplier(Transaction& group, TableInfoCache& table_info_cache) noexcept: + m_transaction(group), + m_table_info_cache(table_info_cache) +{ +} + +inline void InstructionApplier::begin_apply(const Changeset& log, util::Logger* logger) noexcept +{ + m_log = &log; + m_logger = logger; +} + +inline void InstructionApplier::end_apply() noexcept +{ + m_log = nullptr; + m_logger = nullptr; + m_selected_table = TableRef{}; + m_selected_array.reset(); + m_link_target_table = TableRef{}; +} + +template +inline void InstructionApplier::apply(A& applier, const Changeset& changeset, util::Logger* logger) +{ + applier.begin_apply(changeset, logger); + for (auto instr : changeset) { + if (!instr) + continue; + instr->visit(applier); // Throws +#if REALM_DEBUG + applier.m_table_info_cache.verify(); +#endif + } + applier.end_apply(); +} + +template +inline void InstructionApplier::apply(A& applier, Changeset& changeset, util::Logger* logger) +{ + applier.begin_apply(changeset, logger); + for (auto instr : changeset) { + if (!instr) + continue; + instr->visit(applier); // Throws +#if REALM_DEBUG + applier.m_table_info_cache.verify(); +#endif + } + applier.end_apply(); +} + +inline void InstructionApplier::apply(const Changeset& log, util::Logger* logger) +{ + apply(*this, log, logger); // Throws +} + +} // namespace sync +} // namespace realm + +#endif // REALM_SYNC_IMPL_INSTRUCTION_APPLIER_HPP diff --git a/src/vendor-include/realm-ios/include/realm/sync/instruction_replication.hpp b/src/vendor-include/realm-ios/include/realm/sync/instruction_replication.hpp new file mode 100644 index 000000000..1941256d9 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/sync/instruction_replication.hpp @@ -0,0 +1,260 @@ +/************************************************************************* + * + * 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_SYNC_IMPL_INSTRUCTION_REPLICATION_HPP +#define REALM_SYNC_IMPL_INSTRUCTION_REPLICATION_HPP + +#include +#include +#include +#include + +namespace realm { +namespace sync { + + +class SyncReplication: public TrivialReplication { +public: + explicit SyncReplication(const std::string& realm_path); + enum class TableBehavior { + Class, + Ignore + }; + void set_short_circuit(bool) noexcept; + bool is_short_circuited() const noexcept; + + // reset() resets the encoder, the selected tables and the cache. It is + // called by do_initiate_transact(), but can be called at the other times + // as well. + virtual void reset(); + + ChangesetEncoder& get_instruction_encoder() noexcept; + const ChangesetEncoder& get_instruction_encoder() const noexcept; + + //@{ + /// Generate instructions for Object Store tables. These must be called + /// prior to calling the equivalent functions in Core's API. When creating a + /// class-like table, `add_class()` must be called prior to + /// `Group::insert_group_level_table()`. Similarly, `create_object()` or + /// `create_object_with_primary_key()` must be called prior to + /// `Table::insert_empty_row()` and/or `Table::set_int_unique()` or + /// `Table::set_string_unique()` or `Table::set_null_unique()`. + /// + /// If a class-like table is added, or an object-like row is inserted, + /// without calling these methods first, an exception will be thrown. + /// + /// A "class-like table" is defined as a table whose name begins with + /// "class_" (this is the convention used by Object Store). Non-class-like + /// tables can be created and modified using Core's API without calling + /// these functions, because they do not result in instructions being + /// emitted. + void add_class(StringData table_name) override; + void add_class_with_primary_key(StringData table_name, DataType pk_type, StringData pk_field, bool nullable) override; + void create_object(const Table*, GlobalKey) override; + void create_object_with_primary_key(const Table*, GlobalKey, Mixed) override; + void prepare_erase_table(StringData table_name); + //@} + + // TrivialReplication interface: + void initialize(DB&) override; + + // TransactLogConvenientEncoder interface: + void insert_group_level_table(TableKey table_key, size_t num_tables, StringData name) override; + void erase_group_level_table(TableKey table_key, size_t num_tables) override; + void rename_group_level_table(TableKey table_key, StringData new_name) override; + void insert_column(const Table*, ColKey col_key, DataType type, StringData name, LinkTargetInfo& link, + bool nullable, bool Lsttype, LinkType) override; + void erase_column(const Table*, ColKey col_key) override; + void rename_column(const Table*, ColKey col_key, StringData name) override; + + void set_int(const Table*, ColKey col_key, ObjKey key, int_fast64_t value, + _impl::Instruction variant) override; + void add_int(const Table*, ColKey col_key, ObjKey key, int_fast64_t value) override; + void set_bool(const Table*, ColKey col_key, ObjKey key, bool value, _impl::Instruction variant) override; + void set_float(const Table*, ColKey col_key, ObjKey key, float value, _impl::Instruction variant) override; + void set_double(const Table*, ColKey col_key, ObjKey key, double value, _impl::Instruction variant) override; + void set_string(const Table*, ColKey col_key, ObjKey key, StringData value, + _impl::Instruction variant) override; + void set_binary(const Table*, ColKey col_key, ObjKey key, BinaryData value, + _impl::Instruction variant) override; + void set_timestamp(const Table*, ColKey col_key, ObjKey key, Timestamp value, + _impl::Instruction variant) override; + void set_link(const Table*, ColKey col_key, ObjKey key, ObjKey value, _impl::Instruction variant) override; + void set_null(const Table*, ColKey col_key, ObjKey key, _impl::Instruction variant) override; + void insert_substring(const Table*, ColKey col_key, ObjKey key, size_t pos, StringData) override; + void erase_substring(const Table*, ColKey col_key, ObjKey key, size_t pos, size_t size) override; + + void list_set_null(const ConstLstBase& list, size_t ndx) override; + void list_set_int(const ConstLstBase& Lst, size_t list_ndx, int64_t value) override; + void list_set_bool(const ConstLstBase& Lst, size_t list_ndx, bool value) override; + void list_set_float(const ConstLstBase& Lst, size_t list_ndx, float value) override; + void list_set_double(const ConstLstBase& Lst, size_t list_ndx, double value) override; + void list_set_string(const Lst& Lst, size_t list_ndx, StringData value) override; + void list_set_binary(const Lst& Lst, size_t list_ndx, BinaryData value) override; + void list_set_timestamp(const Lst& Lst, size_t list_ndx, Timestamp value) override; + + void list_insert_int(const ConstLstBase& Lst, size_t list_ndx, int64_t value) override; + void list_insert_bool(const ConstLstBase& Lst, size_t list_ndx, bool value) override; + void list_insert_float(const ConstLstBase& Lst, size_t list_ndx, float value) override; + void list_insert_double(const ConstLstBase& Lst, size_t list_ndx, double value) override; + void list_insert_string(const Lst& Lst, size_t list_ndx, StringData value) override; + void list_insert_binary(const Lst& Lst, size_t list_ndx, BinaryData value) override; + void list_insert_timestamp(const Lst& Lst, size_t list_ndx, Timestamp value) override; + + void create_object(const Table*, ObjKey) override; + void remove_object(const Table*, ObjKey) override; + /// \param prior_num_rows The number of rows in the table prior to the + /// modification. + void set_link_type(const Table*, ColKey col_key, LinkType) override; + void clear_table(const Table*, size_t prior_num_rows) override; + + void list_insert_null(const ConstLstBase&, size_t ndx) override; + void list_set_link(const Lst&, size_t link_ndx, ObjKey value) override; + void list_insert_link(const Lst&, size_t link_ndx, ObjKey value) override; + void list_move(const ConstLstBase&, size_t from_link_ndx, size_t to_link_ndx) override; + void list_swap(const ConstLstBase&, size_t link_ndx_1, size_t link_ndx_2) override; + void list_erase(const ConstLstBase&, size_t link_ndx) override; + void list_clear(const ConstLstBase&) override; + + //@{ + + /// 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. + + void nullify_link(const Table*, ColKey col_key, ObjKey key) override; + void link_list_nullify(const Lst&, size_t link_ndx) override; + //@} + + template + void emit(T instruction); + + TableBehavior select_table(const Table*); + const Table* selected_table() const noexcept; + +protected: + // Replication interface: + void do_initiate_transact(Group& group, version_type current_version, bool history_updated) override; +private: + bool m_short_circuit = false; + + ChangesetEncoder m_encoder; + DB* m_sg = nullptr; + std::unique_ptr m_cache; + + // FIXME: The base class already caches this. + const Table* m_selected_table = nullptr; + TableBehavior m_selected_table_behavior; // cache + std::pair m_selected_list; + + // Consistency checks: + std::string m_table_being_created; + std::string m_table_being_created_primary_key; + std::string m_table_being_erased; + util::Optional m_object_being_created; + + REALM_NORETURN void unsupported_instruction(); // Throws TransformError + TableBehavior select_table_inner(const Table* table); + bool select_list(const ConstLstBase&); // returns true if table behavior != ignored + + TableBehavior get_table_behavior(const Table*) const; + + template + void set(const Table*, ColKey col_key, ObjKey row_ndx, T payload, + _impl::Instruction variant); + template + void list_set(const ConstLstBase& Lst, size_t ndx, T payload); + template + void list_insert(const ConstLstBase& Lst, size_t ndx, T payload); + template + void set_pk(const Table*, ColKey col_key, ObjKey row_ndx, T payload, + _impl::Instruction variant); + template + auto as_payload(T value); +}; + +inline void SyncReplication::set_short_circuit(bool b) noexcept +{ + m_short_circuit = b; +} + +inline bool SyncReplication::is_short_circuited() const noexcept +{ + return m_short_circuit; +} + +inline ChangesetEncoder& SyncReplication::get_instruction_encoder() noexcept +{ + return m_encoder; +} + +inline const ChangesetEncoder& SyncReplication::get_instruction_encoder() const noexcept +{ + return m_encoder; +} + +template +inline void SyncReplication::emit(T instruction) +{ + REALM_ASSERT(!m_short_circuit); + m_encoder(instruction); +} + +inline auto SyncReplication::select_table(const Table* table) -> TableBehavior +{ + if (m_selected_table == table) { + return m_selected_table_behavior; + } + return select_table_inner(table); +} + +inline const Table* SyncReplication::selected_table() const noexcept +{ + return m_selected_table; +} + +// Temporarily short-circuit replication +class TempShortCircuitReplication { +public: + TempShortCircuitReplication(SyncReplication& bridge): m_bridge(bridge) + { + m_was_short_circuited = bridge.is_short_circuited(); + bridge.set_short_circuit(true); + } + + ~TempShortCircuitReplication() + { + m_bridge.set_short_circuit(m_was_short_circuited); + } + + bool was_short_circuited() const noexcept + { + return m_was_short_circuited; + } +private: + SyncReplication& m_bridge; + bool m_was_short_circuited; +}; + +} // namespace sync +} // namespace realm + +#endif // REALM_SYNC_IMPL_INSTRUCTION_REPLICATION_HPP diff --git a/src/vendor-include/realm-ios/include/realm/sync/instructions.hpp b/src/vendor-include/realm-ios/include/realm/sync/instructions.hpp new file mode 100644 index 000000000..30ea3e039 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/sync/instructions.hpp @@ -0,0 +1,420 @@ +/************************************************************************* + * + * 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 +#include +#include // string conversion, debug prints +#include // shared_ptr +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace realm { +namespace sync { + +// CAUTION: Any change to the order or number of instructions is a +// protocol-breaking change! +#define REALM_FOR_EACH_INSTRUCTION_TYPE(X) \ + X(SelectTable) \ + X(SelectField) \ + X(AddTable) \ + X(EraseTable) \ + X(CreateObject) \ + X(EraseObject) \ + X(Set) \ + X(AddInteger) \ + X(InsertSubstring) \ + X(EraseSubstring) \ + X(ClearTable) \ + X(AddColumn) \ + X(EraseColumn) \ + X(ArraySet) \ + X(ArrayInsert) \ + X(ArrayMove) \ + X(ArraySwap) \ + X(ArrayErase) \ + X(ArrayClear) \ + + +enum class ContainerType { + None = 0, + Reserved0 = 1, + Array = 2, + Set = 3, + Dictionary = 4, +}; + +struct Instruction { + // Base classes for instructions with common fields. They enable the merge + // algorithm to reuse some code without resorting to templates, and can be + // combined to allow optimal memory layout of instructions (size <= 64). + struct PayloadInstructionBase; + struct ObjectInstructionBase; + struct FieldInstructionBase; + +#define REALM_DECLARE_INSTRUCTION_STRUCT(X) struct X; + REALM_FOR_EACH_INSTRUCTION_TYPE(REALM_DECLARE_INSTRUCTION_STRUCT) +#undef REALM_DECLARE_INSTRUCTION_STRUCT + + enum class Type: uint8_t { +#define REALM_DEFINE_INSTRUCTION_TYPE(X) X, + REALM_FOR_EACH_INSTRUCTION_TYPE(REALM_DEFINE_INSTRUCTION_TYPE) +#undef REALM_DEFINE_INSTRUCTION_TYPE + }; + + struct Payload; + template struct GetType; + template struct GetInstructionType; + + Instruction() {} + template Instruction(T instr); + + static const size_t max_instruction_size = 64; + std::aligned_storage_t m_storage; + Type type; + + template auto visit(F&& lambda); + template auto visit(F&& lambda) const; + + template T& get_as() + { + REALM_ASSERT(type == GetInstructionType::value); + return *reinterpret_cast(&m_storage); + } + + template const T& get_as() const + { + return const_cast(this)->template get_as(); + } + + bool operator==(const Instruction& other) const noexcept; + + bool operator!=(const Instruction& other) const noexcept + { + return !(*this == other); + } +}; + +// 0x3f is the largest value that fits in a single byte in the variable-length +// encoded integer instruction format. +static constexpr uint8_t InstrTypeInternString = 0x3f; + +// This instruction code is only ever used internally by the Changeset class +// to allow insertion/removal while keeping iterators stable. Should never +// make it onto the wire. +static constexpr uint8_t InstrTypeMultiInstruction = 0xff; + +struct StringBufferRange { + uint32_t offset, size; +}; + +struct InternString { + static const InternString npos; + explicit constexpr InternString(uint32_t v = uint32_t(-1)) noexcept : value(v) {} + + uint32_t value; + + bool operator==(const InternString& other) const noexcept { return value == other.value; } + bool operator<(const InternString& other) const noexcept { return value < other.value; } + + explicit operator bool() const noexcept { return (value != npos.value); } +}; + +struct Instruction::Payload { + struct Link { + GlobalKey target; // can be nothing = null + InternString target_table; + }; + + union Data { + bool boolean; + int64_t integer; + float fnum; + double dnum; + StringBufferRange str; + Timestamp timestamp; + Link link; + + Data() noexcept {} + Data(const Data&) noexcept = default; + Data& operator=(const Data&) noexcept = default; + }; + Data data; + int8_t type; // -1 = null, -2 = implicit_nullify + + Payload(): Payload(realm::util::none) {} + explicit Payload(bool value) noexcept: type(type_Bool) { data.boolean = value; } + explicit Payload(int64_t value) noexcept: type(type_Int) { data.integer = value; } + explicit Payload(float value) noexcept: type(type_Float) { data.fnum = value; } + explicit Payload(double value) noexcept: type(type_Double) { data.dnum = value; } + explicit Payload(Link value) noexcept: type(type_Link) { data.link = value; } + explicit Payload(StringBufferRange value) noexcept: type(type_String) { data.str = value; } + explicit Payload(realm::util::None, bool implicit_null = false) noexcept { + type = (implicit_null ? -2 : -1); + } + explicit Payload(Timestamp value) noexcept: type(value.is_null() ? -1 : type_Timestamp) + { + data.timestamp = value; + } + + Payload(const Payload&) noexcept = default; + Payload& operator=(const Payload&) noexcept = default; + + bool is_null() const; + bool is_implicit_null() const; +}; + +struct Instruction::ObjectInstructionBase { + GlobalKey object; +}; + +struct Instruction::FieldInstructionBase + : Instruction::ObjectInstructionBase +{ + InternString field; +}; + +struct Instruction::PayloadInstructionBase { + Payload payload; +}; + + +struct Instruction::SelectTable { + InternString table; +}; + +struct Instruction::SelectField + : Instruction::FieldInstructionBase +{ + InternString link_target_table; +}; + +struct Instruction::AddTable { + InternString table; + InternString primary_key_field; + DataType primary_key_type; + bool has_primary_key; + bool primary_key_nullable; +}; + +struct Instruction::EraseTable { + InternString table; +}; + +struct Instruction::CreateObject + : Instruction::PayloadInstructionBase + , Instruction::ObjectInstructionBase +{ + bool has_primary_key; +}; + +struct Instruction::EraseObject + : Instruction::ObjectInstructionBase +{}; + +struct Instruction::Set + : Instruction::PayloadInstructionBase + , Instruction::FieldInstructionBase +{ + bool is_default; +}; + +struct Instruction::AddInteger + : Instruction::FieldInstructionBase +{ + int64_t value; +}; + +struct Instruction::InsertSubstring + : Instruction::FieldInstructionBase +{ + StringBufferRange value; + uint32_t pos; +}; + +struct Instruction::EraseSubstring + : Instruction::FieldInstructionBase +{ + uint32_t pos; + uint32_t size; +}; + +struct Instruction::ClearTable { +}; + +struct Instruction::ArraySet { + Instruction::Payload payload; + uint32_t ndx; + uint32_t prior_size; +}; + +struct Instruction::ArrayInsert { + // payload carries the value in case of LinkList + // payload is empty in case of Array, Dict or any other container type + Instruction::Payload payload; + uint32_t ndx; + uint32_t prior_size; +}; + +struct Instruction::ArrayMove { + uint32_t ndx_1; + uint32_t ndx_2; +}; + +struct Instruction::ArrayErase { + uint32_t ndx; + uint32_t prior_size; + bool implicit_nullify; +}; + +struct Instruction::ArraySwap { + uint32_t ndx_1; + uint32_t ndx_2; +}; + +struct Instruction::ArrayClear { + uint32_t prior_size; +}; + + +// If container_type != ContainerType::none, creates a subtable: +// +---+---+-------+ +// | a | b | c | +// +---+---+-------+ +// | | | +---+ | +// | | | | v | | +// | | | +---+ | +// | 1 | 2 | | 3 | | +// | | | | 4 | | +// | | | | 5 | | +// | | | +---+ | +// +---+---+-------+ +struct Instruction::AddColumn { + InternString field; + InternString link_target_table; + DataType type; + ContainerType container_type; + bool nullable; +}; + +struct Instruction::EraseColumn { + InternString field; +}; + +struct InstructionHandler { + /// Notify the handler that an InternString meta-instruction was found. + virtual void set_intern_string(uint32_t index, StringBufferRange) = 0; + + /// Notify the handler of the string value. The handler guarantees that the + /// returned string range is valid at least until the next invocation of + /// add_string_range(). + /// + /// Instances of `StringBufferRange` passed to operator() after invoking + /// this function are assumed to refer to ranges in this buffer. + virtual StringBufferRange add_string_range(StringData) = 0; + + /// Handle an instruction. + virtual void operator()(const Instruction&) = 0; +}; + + +/// Implementation: + +#define REALM_DEFINE_INSTRUCTION_GET_TYPE(X) \ + template <> struct Instruction::GetType { using Type = Instruction::X; }; \ + template <> struct Instruction::GetInstructionType { static const Instruction::Type value = Instruction::Type::X; }; + REALM_FOR_EACH_INSTRUCTION_TYPE(REALM_DEFINE_INSTRUCTION_GET_TYPE) +#undef REALM_DEFINE_INSTRUCTION_GET_TYPE + +template +Instruction::Instruction(T instr) : type(GetInstructionType::value) +{ + new(&m_storage) T(std::move(instr)); +} + +template +inline auto Instruction::visit(F&& lambda) +{ + switch (type) { +#define REALM_VISIT_INSTRUCTION(X) \ + case Type::X: \ + return lambda(get_as()); + REALM_FOR_EACH_INSTRUCTION_TYPE(REALM_VISIT_INSTRUCTION) +#undef REALM_VISIT_INSTRUCTION + } + REALM_UNREACHABLE(); +} + +template +inline auto Instruction::visit(F&& lambda) const +{ + switch (type) { +#define REALM_VISIT_INSTRUCTION(X) \ + case Type::X: \ + return lambda(get_as()); + REALM_FOR_EACH_INSTRUCTION_TYPE(REALM_VISIT_INSTRUCTION) +#undef REALM_VISIT_INSTRUCTION + } + REALM_UNREACHABLE(); +} + +inline bool Instruction::operator==(const Instruction& other) const noexcept +{ + if (type != other.type) + return false; + size_t valid_size; + switch (type) { +#define REALM_COMPARE_INSTRUCTION(X) \ + case Type::X: valid_size = sizeof(Instruction::X); break; + REALM_FOR_EACH_INSTRUCTION_TYPE(REALM_COMPARE_INSTRUCTION) +#undef REALM_COMPARE_INSTRUCTION + default: REALM_UNREACHABLE(); + } + + // This relies on all instruction types being PODs to work. + return std::memcmp(&m_storage, &other.m_storage, valid_size) == 0; +} + +inline bool Instruction::Payload::is_null() const +{ + return type < 0; +} + +inline bool Instruction::Payload::is_implicit_null() const +{ + return type == -2; +} + +std::ostream& operator<<(std::ostream&, Instruction::Type); + +} // namespace _impl +} // namespace realm + +#endif // REALM_IMPL_INSTRUCTIONS_HPP diff --git a/src/vendor-include/realm-ios/include/realm/sync/object.hpp b/src/vendor-include/realm-ios/include/realm/sync/object.hpp new file mode 100644 index 000000000..5e0fef4bb --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/sync/object.hpp @@ -0,0 +1,288 @@ +/************************************************************************* + * + * 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_SYNC_OBJECT_HPP +#define REALM_SYNC_OBJECT_HPP + +#include +#include +#include +#include + +#include + +#include + +/// This file presents a convenience API for making changes to a Realm file that +/// adhere to the conventions of assigning stable IDs to every object. + +namespace realm { + +class Group; +class ReadTransaction; +class WriteTransaction; + +namespace sync { + +class SyncHistory; +struct TableInfoCache; + +/// Determine whether the Group has a sync-type history, and therefore whether +/// it supports globally stable object IDs. +/// +/// The Group does not need to be in a transaction. +bool has_object_ids(const Table&); + +/// Determine whether object IDs for objects without primary keys are globally +/// stable. This is true if and only if the Group has been in touch with the +/// server (or is the server), and will remain true forever thereafter. +/// +/// It is an error to call this function for groups that do not have object IDs +/// (i.e. where `has_object_ids()` returns false). +/// +/// The Group is assumed to be in a read transaction. +bool is_object_id_stability_achieved(const DB&, const Transaction&); + +/// Create a table with an object ID column. +/// +/// It is an error to add tables to Groups with a sync history type directly. +/// This function or related functions must be used instead. +/// +/// The resulting table will be born with 1 column, which is a column used +/// in the maintenance of object IDs. +/// +/// NOTE: The table name must begin with the prefix "class_" in accordance with +/// Object Store conventions. +/// +/// The Group must be in a write transaction. +inline TableRef create_table(Transaction& wt, StringData name) +{ + return wt.get_or_add_table(name); +} + +/// Create a table with an object ID column and a primary key column. +/// +/// It is an error to add tables to Groups with a sync history type directly. +/// This function or related functions must be used instead. +/// +/// The resulting table will be born with 2 columns, which is a column used +/// in the maintenance of object IDs and the requested primary key column. +/// The primary key column must have either integer or string type, and it +/// will be given the name provided in the argument \a pk_column_name. +/// +/// The 'pk' metadata table is updated with information about the primary key +/// column. If the 'pk' table does not yet exist, it is created. +/// +/// Please note: The 'pk' metadata table will not be synchronized directly, +/// so subsequent updates to it will be lost (as they constitute schema-breaking +/// changes). +/// +/// NOTE: The table name must begin with the prefix "class_" in accordance with +/// Object Store conventions. +/// +/// The Group must be in a write transaction. +inline TableRef create_table_with_primary_key(Transaction& wt, StringData name, DataType pk_type, + StringData pk_column_name, bool nullable = false) +{ + if (TableRef table = wt.get_table(name)) { + if (!table->get_primary_key_column() || + table->get_column_name(table->get_primary_key_column()) != pk_column_name || + table->is_nullable(table->get_primary_key_column()) != nullable) { + throw std::runtime_error("Inconsistent schema"); + } + return table; + } + return wt.add_table_with_primary_key(name, pk_type, pk_column_name, nullable); +} + + +//@{ +/// Erase table and update metadata. +/// +/// It is an error to erase tables via the Group API, because it does not +/// correctly update metadata tables (such as the `pk` table). +void erase_table(Transaction&, TableInfoCache& table_info_cache, StringData name); +void erase_table(Transaction&, TableInfoCache& table_info_cache, TableRef); +//@} + +/// Create an array column with the specified element type. +/// +/// The result will be a column of type type_Table with one subcolumn named +/// "!ARRAY_VALUE" of the specified element type and nullability. +/// +/// Return the column index of the inserted array column. +ColKey add_array_column(Table&, DataType element_type, StringData column_name, bool is_nullable = false); + + +/// Determine whether it is safe to call `object_id_for_row()` on tables without +/// primary keys. If the table has a primary key, always returns true. +bool has_globally_stable_object_ids(const Table&); + +bool table_has_primary_key(const TableInfoCache&, const Table&); + +/// Get the globally unique object ID for the row. +/// +/// If the table has a primary key, this is guaranteed to succeed. Otherwise, if +/// the server has not been contacted yet (`has_globally_stable_object_ids()` +/// returns false), an exception is thrown. +GlobalKey object_id_for_row(const TableInfoCache&, const Table&, ObjKey); +GlobalKey object_id_for_row(const TableInfoCache&, const ConstObj&); + +/// Get the index of the row with the object ID. +/// +/// \returns realm::npos if the object does not exist in the table. +ObjKey row_for_object_id(const TableInfoCache&, const Table&, GlobalKey); +Obj obj_for_object_id(const TableInfoCache&, Table&, GlobalKey); +ConstObj obj_for_object_id(const TableInfoCache&, const Table&, GlobalKey); + +//@{ +/// Add a row to the table and populate the object ID with an appropriate value. +/// +/// In the variant which takes an GlobalKey parameter, a check is performed to see +/// if the object already exists. If it does, the row index of the existing object +/// is returned. +/// +/// If the table has a primary key column, an exception is thrown. +/// +/// \returns the row index of the object. +inline Obj create_object(const TableInfoCache&, Table& t) +{ + return t.create_object(); +} + +inline Obj create_object(const TableInfoCache&, Table& t, GlobalKey object_id) +{ + return t.create_object(object_id); +} +//@} + +//@{ +/// Create an object with a primary key value and populate the object ID with an +/// appropriate value. +/// +/// If the table does not have a primary key column (as indicated by the Object +/// Store's metadata in the special "pk" table), or the type of the primary key +/// column does not match the argument provided, an exception is thrown. +/// +/// The primary key column's value is populated with the appropriate +/// `set_int_unique()`, `set_string_unique()`, or `set_null_unique()` method +/// called on \a table. +/// +/// If an object with the given primary key value already exists, its row number +/// is returned without creating any new objects. +/// +/// These are convenience functions, equivalent to the following: +/// - Add an empty row to the table. +/// - Obtain an `GlobalKey` with `object_id_for_primary_key()`. +/// - Obtain a local object ID with `global_to_local_object_id()`. +/// - Store the local object ID in the object ID column. +/// - Call `set_int_unique()`,`set_string_unique()`, or `set_null_unique()` +/// to set the primary key value. +/// +/// \returns the row index of the created object. +Obj create_object_with_primary_key(const TableInfoCache&, Table&, util::Optional primary_key); +Obj create_object_with_primary_key(const TableInfoCache&, Table&, StringData primary_key); +Obj create_object_with_primary_key(const TableInfoCache&, Table&, int64_t primary_key); +//@} + +struct TableInfoCache { + const Transaction& m_transaction; + + // Implicit conversion deliberately allowed for the purpose of calling the above + // functions without constructing a cache manually. + TableInfoCache(const Transaction&); + TableInfoCache(WriteTransaction&); + TableInfoCache(ReadTransaction&); + TableInfoCache(TableInfoCache&&) noexcept = default; + + struct TableInfo { + struct VTable; + + TableKey key; + StringData name; + ColKey primary_key_col; + + ConstTableRef get_table(const Transaction&) const; + TableRef get_table(Transaction&) const; + bool primary_key_nullable; + DataType primary_key_type; + mutable ObjKey last_obj_key; + mutable GlobalKey last_object_id; + + void clear_last_object() const + { + last_obj_key = realm::null_key; + last_object_id = {}; + } + }; + + mutable std::map m_table_info; + + const TableInfo& get_table_info(const Table&) const; + const TableInfo& get_table_info(TableKey) const; + void clear(); + void clear_last_object(const Table&); + void verify(); +}; + + +/// Migrate a server-side Realm file whose history type is +/// `Replication::hist_SyncServer` and whose history schema version is 0 (i.e., +/// Realm files without stable identifiers). +void import_from_legacy_format(const Group& old_group, Group& new_group, util::Logger&); + +using TableNameBuffer = std::array; +StringData table_name_to_class_name(StringData); +StringData class_name_to_table_name(StringData, TableNameBuffer&); + + +// Implementation: + +inline StringData table_name_to_class_name(StringData table_name) +{ + REALM_ASSERT(table_name.begins_with("class_")); + return table_name.substr(6); +} + + +inline StringData class_name_to_table_name(StringData class_name, TableNameBuffer& buffer) +{ + constexpr const char class_prefix[] = "class_"; + constexpr size_t class_prefix_len = sizeof(class_prefix) - 1; + char* p = std::copy_n(class_prefix, class_prefix_len, buffer.data()); + size_t len = std::min(class_name.size(), buffer.size() - class_prefix_len); + std::copy_n(class_name.data(), len, p); + return StringData(buffer.data(), class_prefix_len + len); +} + +inline TableInfoCache::TableInfoCache(WriteTransaction& wt) + : TableInfoCache(wt.operator Transaction&()) +{ +} + +inline TableInfoCache::TableInfoCache(ReadTransaction& rt) + : TableInfoCache(rt.operator Transaction&()) +{ +} + +} // namespace sync +} // namespace realm + +#endif // REALM_SYNC_OBJECT_HPP + + diff --git a/src/vendor-include/realm-ios/include/realm/sync/object_id.hpp b/src/vendor-include/realm-ios/include/realm/sync/object_id.hpp new file mode 100644 index 000000000..f8843e8fb --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/sync/object_id.hpp @@ -0,0 +1,119 @@ +/************************************************************************* + * + * 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_SYNC_OBJECT_ID_HPP +#define REALM_SYNC_OBJECT_ID_HPP + +#include // std::hash +#include +#include // operator<< +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace realm { + +class Group; + +namespace sync { + +// ObjectIDSet is a set of (table name, object id) +class ObjectIDSet { +public: + + void insert(StringData table, GlobalKey object_id); + void erase(StringData table, GlobalKey object_id); + bool contains(StringData table, GlobalKey object_id) const noexcept; + bool empty() const noexcept; + + // A map from table name to a set of object ids. + util::metered::map> m_objects; +}; + +// FieldSet is a set of fields in tables. A field is defined by a +// table name, a column in the table and an object id for the row. +class FieldSet { +public: + + void insert(StringData table, StringData column, GlobalKey object_id); + void erase(StringData table, StringData column, GlobalKey object_id); + bool contains(StringData table, GlobalKey object_id) const noexcept; + bool contains(StringData table, StringData column, GlobalKey object_id) const noexcept; + bool empty() const noexcept; + + // A map from table name to a map from column name to a set of + // object ids. + util::metered::map< + std::string, + util::metered::map> + > m_fields; +}; + +struct GlobalID { + StringData table_name; + GlobalKey object_id; + + bool operator==(const GlobalID& other) const; + bool operator!=(const GlobalID& other) const; + bool operator<(const GlobalID& other) const; +}; + +/// Implementation: + + +inline bool GlobalID::operator==(const GlobalID& other) const +{ + return object_id == other.object_id && table_name == other.table_name; +} + +inline bool GlobalID::operator!=(const GlobalID& other) const +{ + return !(*this == other); +} + +inline bool GlobalID::operator<(const GlobalID& other) const +{ + if (table_name == other.table_name) + return object_id < other.object_id; + return table_name < other.table_name; +} + +inline bool ObjectIDSet::empty() const noexcept +{ + return m_objects.empty(); +} + +inline bool FieldSet::empty() const noexcept +{ + return m_fields.empty(); +} + +} // namespace sync +} // namespace realm + +#endif // REALM_SYNC_OBJECT_ID_HPP + diff --git a/src/vendor-include/realm-ios/include/realm/sync/permissions.hpp b/src/vendor-include/realm-ios/include/realm/sync/permissions.hpp new file mode 100644 index 000000000..4c1797541 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/sync/permissions.hpp @@ -0,0 +1,453 @@ +/************************************************************************* + * + * 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_PERMISSIONS_HPP +#define REALM_SYNC_PERMISSIONS_HPP + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace realm { +namespace sync { + +/// Permissions Schema: +/// +/// class___Role: +/// string name PRIMARY_KEY; +/// User[] members; +/// +/// class___Permission: +/// __Role role; +/// bool canRead; +/// bool canUpdate; +/// bool canDelete; +/// bool canSetPermissions; +/// bool canQuery; +/// bool canCreate; +/// bool canModifySchema; +/// +/// class___Realm: +/// int id PRIMARY_KEY = 0; // singleton object +/// __Permission[] permissions; +/// +/// class___User: +/// string id PRIMARY_KEY; +/// __Role role; +/// +/// class___Class: +/// string name PRIMARY_KEY; +/// __Permission[] permissions; +/// +/// class_: +/// __Permission[] ; +/// __Role ; +/// + +static constexpr char g_roles_table_name[] = "class___Role"; +static constexpr char g_permissions_table_name[] = "class___Permission"; +static constexpr char g_users_table_name[] = "class___User"; +static constexpr char g_classes_table_name[] = "class___Class"; +static constexpr char g_realms_table_name[] = "class___Realm"; + + +/// Create the permissions schema if it doesn't already exist. +void create_permissions_schema(Transaction&); + +/// Set up the basic "everyone" role and default permissions. The default is to +/// set up some very permissive defaults, where "everyone" can do everything. +void set_up_basic_permissions(Transaction&, TableInfoCache& table_info_cache, bool permissive = true); +// Convenience function that creates a new TableInfoCache. +void set_up_basic_permissions(Transaction&, bool permissive = true); + +/// Set up some basic permissions for the class. The default is to set up some +/// very permissive default, where "everyone" can do everything in the class. +void set_up_basic_permissions_for_class(Transaction&, StringData class_name, bool permissive = true); +// void set_up_basic_default_permissions_for_class(Group&, TableRef klass, bool permissive = true); + +/// Return the index of the ACL in the class, if one exists. If no ACL column is +/// defined in the class, returns `npos`. +ColKey find_permissions_column(const Transaction&, ConstTableRef); + +//@{ +/// Convenience functions to check permisions data +/// The functions must be called inside a read (or write) transaction. +bool permissions_schema_exist(const Transaction&); + +bool user_exist(const Transaction&, StringData user_id); +//@} + + +/// Perform a query as user \a user_id, returning only the results that the +/// user has access to read. If the user is an admin, there is no need to call +/// this function, since admins can always read everything. +/// +/// If the target table of the query does not have object-level permissions, +/// the query results will be returned without any additional filtering. +/// +/// If the target table of the query has object-level permissions, but the +/// permissions schema of this Realm is invalid, an exception of type +/// `InvalidPermissionsSchema` is thrown. +/// +/// LIMIT and DISTINCT will be applied *after* permission filters. +/// +/// The resulting TableView can be used like any other query result. +/// +/// Note: Class-level and Realm-level permissions are not taken into account in +/// the resulting TableView, since there is no way to represent this in the +/// query engine. +ConstTableView query_with_permissions(Query query, StringData user_id, + const DescriptorOrdering* ordering = nullptr); + +struct InvalidPermissionsSchema : util::runtime_error { + using util::runtime_error::runtime_error; +}; + +//@{ +/// Convenience function to modify permission data. +/// +/// When a role or user has not already been defined in the Realm, these +/// functions create them on-demand. +void set_realm_permissions_for_role(Transaction&, StringData role_name, + uint_least32_t privileges); +void set_realm_permissions_for_role(Transaction&, ObjKey role, + uint_least32_t privileges); +void set_class_permissions_for_role(Transaction&, StringData class_name, + StringData role_name, uint_least32_t privileges); +void set_class_permissions_for_role(Transaction&, StringData class_name, ObjKey role_key, + uint_least32_t privileges); +// void set_default_object_permissions_for_role(Group&, StringData class_name, +// StringData role_name, +// uint_least32_t privileges); +void set_object_permissions_for_role(Transaction&, TableRef table, Obj& object, + StringData role_name, uint_least32_t privileges); +void set_object_permissions_for_role(Transaction&, TableRef table, Obj& object, ObjKey role, + uint_least32_t privileges); + +void add_user_to_role(Transaction&, StringData user_id, StringData role_name); +void add_user_to_role(Transaction&, Obj user, ObjKey role); +//@} + +/// The Privilege enum is intended to be used in a bitfield. +enum class Privilege : uint_least32_t { + None = 0, + + /// The user can read the object (i.e. it can participate in the user's + /// subscription. + /// + /// NOTE: On objects, it is a prerequisite that the object's class is also + /// readable by the user. + /// + /// FIXME: Until we get asynchronous links, any object that is reachable + /// through links from another readable/queryable object is also readable, + /// regardless of whether the user specifically does not have read access. + Read = 1, + + /// The user can modify the fields of the object. + /// + /// NOTE: On objects, it is a prerequisite that the object's class is also + /// updatable by the user. When applied to a Class object, it does not + /// imply that the user can modify the schema of the class, only the + /// objects of that class. + /// + /// NOTE: This does not imply the SetPermissions privilege. + Update = 2, + + /// The user can delete the object. + /// + /// NOTE: When applied to a Class object, it has no effect on whether + /// objects of that class can be deleted by the user. + /// + /// NOTE: This implies the ability to implicitly nullify links pointing + /// to the object from other objects, even if the user does not have + /// permission to modify those objects in the normal way. + Delete = 4, + + //@{ + /// The user can modify the object's permissions. + /// + /// NOTE: The user will only be allowed to assign permissions at or below + /// their own privilege level. + SetPermissions = 8, + Share = SetPermissions, + //@} + + /// When applied to a Class object, the user can query objects in that + /// class. + /// + /// Has no effect when applied to objects other than Class. + Query = 16, + + /// When applied to a Class object, the user may create objects in that + /// class. + /// + /// NOTE: The user implicitly has Update and SetPermissions + /// (but not necessarily Delete permission) within the same + /// transaction as the object was created. + /// + /// NOTE: Even when a user has CreateObject rights, a CreateObject + /// operation may still be rejected by the server, if the object has a + /// primary key and the object already exists, but is not accessible by the + /// user. + Create = 32, + + /// When applied as a "Realm" privilege, the user can add classes and add + /// columns to classes. + /// + /// NOTE: When applied to a class or object, this has no effect. + ModifySchema = 64, + + /// + /// Aggregate permissions for compatibility: + /// + Download = Read | Query, + Upload = Update | Delete | Create, + DeleteRealm = Upload, // FIXME: This seems overly permissive +}; + +inline constexpr uint_least32_t operator|(Privilege a, Privilege b) +{ + return static_cast(a) | static_cast(b); +} + +inline constexpr uint_least32_t operator|(uint_least32_t a, Privilege b) +{ + return a | static_cast(b); +} + +inline constexpr uint_least32_t operator&(Privilege a, Privilege b) +{ + return static_cast(a) & static_cast(b); +} + +inline constexpr uint_least32_t operator&(uint_least32_t a, Privilege b) +{ + return a & static_cast(b); +} + +inline uint_least32_t& operator|=(uint_least32_t& a, Privilege b) +{ + return a |= static_cast(b); +} + +inline constexpr uint_least32_t operator~(Privilege p) +{ + return ~static_cast(p); +} + +struct PermissionsCache { + /// Each element is the index of a row in the `class___Roles` table. + using RoleList = std::vector; + + PermissionsCache(const Transaction& g, TableInfoCache& table_info_cache, StringData user_identity, bool is_admin = false); + + bool is_admin() const noexcept; + + /// Leaves out any role that has no permission objects linking to it. + RoleList get_users_list_of_roles(); + + /// Get Realm-level privileges for the current user. + /// + /// The user must have Read access at the Realm level to be able to see + /// anything in the file. + /// + /// The user must have Update access at the Realm level to be able to make + /// any changes at all in the Realm file. + /// + /// If no Realm-level permissions are defined, no access is granted for any + /// user. + uint_least32_t get_realm_privileges(); + + /// Get class-level privileges for the current user and the given class. + /// + /// If the class does not have any class-level privileges defined, no access + /// is granted to the class. + /// + /// Calling this function is equivalent to calling `get_object_privileges()` + /// with an object of the type `__Class`. + /// + /// NOTE: This function only considers class-level permissions. It does not + /// mask the returned value by the Realm-level permissions. See `can()`. + uint_least32_t get_class_privileges(StringData class_name); + + /// Get object-level privileges for the current user and the given object. + /// + /// If the object's class has an ACL property (a linklist to the + /// `__Permission` class), and it isn't empty, the user's privileges is the + /// OR'ed privileges for the intersection of roles that have a defined + /// permission on the object and the roles of which the user is a member. + /// + /// If the object's ACL property is empty (but the column exists), no access + /// is granted to anyone. + /// + /// If the object does not exist in the table, the returned value is + /// equivalent to that of an object with an empty ACL property, i.e. no + /// privileges are granted. Note that the existence of the column is checked + /// first, so an absent ACL property (granting all privileges) takes + /// precedence over an absent object (granting no privileges) in terms of + /// calculating permissions. + /// + /// NOTE: This function only considers object-level permissions (per-object + /// ACLs or default object permissions). It does not mask the returned value + /// by the object's class-level permissions, or by the Realm-level + /// permissions. See `can()`. + uint_least32_t get_object_privileges(GlobalID); + + /// Get object-level privileges without adding it to the cache. + uint_least32_t get_object_privileges_nocache(GlobalID); + + //@{ + /// Check permissions for the object, taking all levels of permission into + /// account. + /// + /// This method only returns `true` if the user has Realm-level access to + /// the object, class-level access to the object, and object-level access to + /// the object. + /// + /// In the version where the first argument is a mask of privileges, the + /// method only returns `true` when all privileges are satisfied. + bool can(Privilege privilege, GlobalID object_id); + bool can(uint_least32_t privileges, GlobalID object_id); + //@} + + /// Invalidate all cache entries pertaining to the object. + /// + /// The object may be an instance of `__Class`. + void object_permissions_modified(GlobalID); + + /// Register the object as created in this transaction, meaning that the + /// user gets full privileges until the end of the transaction. + void object_created(GlobalID); + + /// Invalidate all cache entries pertaining to the class. + // void default_object_permissions_modified(StringData class_name); + + /// Invalidate all cached permissions. + void clear(); + + /// Check that all cache permissions correspond to the current permission + /// state in the database. + void verify(); + +private: + const Transaction& group; + TableInfoCache& m_table_info_cache; + std::string user_id; + bool m_is_admin; + util::Optional realm_privileges; + util::metered::map object_privileges; + ObjectIDSet created_objects; + + // uint_least32_t get_default_object_privileges(ConstTableRef); + uint_least32_t get_privileges_for_permissions(const ConstLnkLst&); + friend struct InstructionApplierWithPermissionCheck; +}; + +inline bool PermissionsCache::is_admin() const noexcept +{ + return m_is_admin; +} + +/// PermissionCorrections is a struct that describes some changes that must be +/// sent to the client because the client tried to perform changes to a database +/// that it wasn't allowed to make. +struct PermissionCorrections { + using TableColumnSet = util::metered::map>; + using TableSet = util::metered::set; + + // Objects that a client tried to delete without being allowed. + ObjectIDSet recreate_objects; + + // Objects that a client tried to create without being allowed. + ObjectIDSet erase_objects; + + // Fields that were illegally modified by the client and must be reset. + // + // Objects mentioned in `recreate_objects` and `erase_objects` are not + // mentioned here. + FieldSet reset_fields; + + // Columns that were illegally added by the client. + TableColumnSet erase_columns; + + // Columns that were illegally removed by the client. + TableColumnSet recreate_columns; + + // Tables that were illegally added by the client. + // std::set erase_tables; + TableSet erase_tables; + + // Tables that were illegally removed by the client. + TableSet recreate_tables; + + bool empty() const noexcept; +}; + +// Function for printing out a permission correction object. Useful for debugging purposes. +std::ostream& operator<<(std::ostream&, const PermissionCorrections&); + + + +/// InstructionApplierWithPermissionCheck conditionally applies each +/// instruction, and builds a `PermissionCorrections` struct based on the +/// illicit changes. The member `m_corrections` can be used to synthesize a +/// changeset that can be sent to the client to revert the illicit changes that +/// were detected by the applier. +struct InstructionApplierWithPermissionCheck { + explicit InstructionApplierWithPermissionCheck(Transaction& reference_realm, + bool is_admin, + StringData user_identity); + ~InstructionApplierWithPermissionCheck(); + + /// Apply \a incoming_changeset, checking permissions in the process. + /// Populates `m_corrections`. + void apply(const Changeset& incoming_changeset, util::Logger*); + + PermissionCorrections m_corrections; + +private: + struct Impl; + std::unique_ptr m_impl; +}; + + +// Implementation: + +inline bool PermissionCorrections::empty() const noexcept +{ + return recreate_objects.empty() && erase_objects.empty() + && reset_fields.empty() && erase_columns.empty() + && recreate_columns.empty() && erase_tables.empty() + && recreate_tables.empty(); +} + +} // namespace sync +} // namespace realm + + +#endif // REALM_SYNC_PERMISSIONS_HPP diff --git a/src/vendor-include/realm-ios/include/realm/sync/protocol.hpp b/src/vendor-include/realm-ios/include/realm/sync/protocol.hpp new file mode 100644 index 000000000..117821897 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/sync/protocol.hpp @@ -0,0 +1,439 @@ +/************************************************************************* + * + * 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 + + + +// 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. +// +// 18 Enhanced the session_ident to accept values of size up to at least 63 bits. +// +// 19 New instruction log format with stable object IDs and arrays of +// primitives (Generalized LinkList* commands to Container* commands) +// Message format is identical to version 18. +// +// 20 Added support for log compaction in DOWNLOAD message. +// +// 21 Removed "class_" prefix in instructions referencing tables. +// +// 22 Fixed a bug in the merge rule of MOVE vs SWAP. +// +// 23 Introduced full support for session specific ERROR messages. Removed the +// obsolete concept of a "server file identifier". Added support for relayed +// subtier client file identifier allocation. For this purpose, the message +// that was formerly known as ALLOC was renamed to IDENT, and a new ALLOC +// message was added in both directions. Added the ability for an UPLOAD +// message to carry a per-changeset origin client file identifier. Added +// `` parameter to DOWNLOAD message. Added new error +// codes 215 "Unsupported session-level feature" and 216 "Bad origin client +// file identifier (UPLOAD)". +// +// 24 Support schema-breaking instructions. Official support for partial sync. +// +// 25 Include "last server version" in the UPLOAD message for history trimming +// on the server. +// +// 26 Four new protocol error codes, 217, 218, 219, and 220. +// +// The downloadable_bytes field in the DOWNLOAD message denotes the byte +// size of the changesets following those in the DOWNLOAD +// message. Previously, downloadable_bytes denoted the total byte size of +// the entire history. +// +// Introduction of protocol version flexibility on client side (strictly +// speaking, this is a change that transcends the sync protocol). +// +// 27 STATE_REQUEST, STATE, CLIENT_VERSION_REQUEST and CLIENT_VERSION messages +// introduced. These messages are used for client reset and async open. +// +// 28 Introduction of TRANSACT message (serialized transactions). New session +// level error code 221 "Serialized transaction before upload completion". +// +// Also added new parameters ``, ``, ``, and `` to STATE_REQUEST message. +// +// 29 Addition of `` and `` +// to the UPLOAD message. Together, these constitute an upload cursor that +// marks the reached position in the client-side history of the uploading +// process. +// +// Removal of ``, and addition of ``. `` was replaced in part by `` as described above, and in part by the new ``. The purpose of `` is to allow +// the client to lock parts of the server-side history that precede +// ``. It is intended to be used in the future in +// conjunction with the cooked history. +// +// 30 New error code, 222 "Client file has expired", was added. New parameter +// `` added to BIND message. +// +constexpr int get_current_protocol_version() noexcept +{ + return 30; +} + + +/// Supported protocol envelopes: +/// +/// Alternative (*) +/// Name Envelope URL scheme Default port default port +/// ------------------------------------------------------------------------ +/// realm WebSocket realm: 7800 80 +/// realms WebSocket + SSL realms: 7801 443 +/// ws WebSocket ws: 80 +/// wss WebSocket + SSL wss: 443 +/// +/// *) When Client::Config::enable_default_port_hack is true +/// +enum class ProtocolEnvelope { realm, realms, ws, wss }; + +inline bool is_ssl(ProtocolEnvelope protocol) noexcept +{ + switch (protocol) { + case ProtocolEnvelope::realm: + case ProtocolEnvelope::ws: + break; + case ProtocolEnvelope::realms: + case ProtocolEnvelope::wss: + return true; + } + return false; +} + + +// These integer types are selected so that they accomodate the requirements of +// the protocol specification (`/doc/protocol.md`). +using file_ident_type = std::uint_fast64_t; +using version_type = Replication::version_type; +using salt_type = std::int_fast64_t; +using timestamp_type = std::uint_fast64_t; +using session_ident_type = std::uint_fast64_t; +using request_ident_type = std::uint_fast64_t; +using milliseconds_type = std::int_fast64_t; + +constexpr file_ident_type get_max_file_ident() +{ + return 0x0'7FFF'FFFF'FFFF'FFFF; +} + + +struct SaltedFileIdent { + file_ident_type ident; + /// History divergence and identity spoofing protection. + salt_type salt; +}; + +struct SaltedVersion { + version_type version; + /// History divergence protection. + salt_type salt; +}; + + +/// \brief A client's reference to a position in the server-side history. +/// +/// A download cursor refers to a position in the server-side history. If +/// `server_version` is zero, the position is at the beginning of the history, +/// otherwise the position is after the entry whose changeset produced that +/// version. In general, positions are to be understood as places between two +/// adjacent history entries. +/// +/// `last_integrated_client_version` is the version produced on the client by +/// the last changeset that was sent to the server and integrated into the +/// server-side Realm state at the time indicated by the history position +/// specified by `server_version`, or zero if no changesets from the client were +/// integrated by the server at that point in time. +struct DownloadCursor { + version_type server_version; + version_type last_integrated_client_version; +}; + +/// Checks that `dc.last_integrated_client_version` is zero if +/// `dc.server_version` is zero. +bool is_consistent(DownloadCursor dc) noexcept; + +/// Checks that `a.last_integrated_client_version` and +/// `b.last_integrated_client_version` are equal, if `a.server_version` and +/// `b.server_version` are equal. Otherwise checks that +/// `a.last_integrated_client_version` is less than, or equal to +/// `b.last_integrated_client_version`, if `a.server_version` is less than +/// `b.server_version`. Otherwise checks that `a.last_integrated_client_version` +/// is greater than, or equal to `b.last_integrated_client_version`. +bool are_mutually_consistent(DownloadCursor a, DownloadCursor b) noexcept; + + +/// \brief The server's reference to a position in the client-side history. +/// +/// An upload cursor refers to a position in the client-side history. If +/// `client_version` is zero, the position is at the beginning of the history, +/// otherwise the position is after the entry whose changeset produced that +/// version. In general, positions are to be understood as places between two +/// adjacent history entries. +/// +/// `last_integrated_server_version` is the version produced on the server by +/// the last changeset that was sent to the client and integrated into the +/// client-side Realm state at the time indicated by the history position +/// specified by `client_version`, or zero if no changesets from the server were +/// integrated by the client at that point in time. +struct UploadCursor { + version_type client_version; + version_type last_integrated_server_version; +}; + +/// Checks that `uc.last_integrated_server_version` is zero if +/// `uc.client_version` is zero. +bool is_consistent(UploadCursor uc) noexcept; + +/// Checks that `a.last_integrated_server_version` and +/// `b.last_integrated_server_version` are equal, if `a.client_version` and +/// `b.client_version` are equal. Otherwise checks that +/// `a.last_integrated_server_version` is less than, or equal to +/// `b.last_integrated_server_version`, if `a.client_version` is less than +/// `b.client_version`. Otherwise checks that `a.last_integrated_server_version` +/// is greater than, or equal to `b.last_integrated_server_version`. +bool are_mutually_consistent(UploadCursor a, UploadCursor b) noexcept; + + +/// A client's record of the current point of progress of the synchronization +/// process. The client must store this persistently in the local Realm file. +struct SyncProgress { + /// The last server version that the client has heard about. + SaltedVersion latest_server_version = {0, 0}; + + /// The last server version integrated, or about to be integrated by the + /// client. + DownloadCursor download = {0, 0}; + + /// The last client version integrated by the server. + UploadCursor upload = {0, 0}; +}; + + +/// An indication of the final status of an attempt at performing a serialized +/// transaction. +enum class SerialTransactStatus { + /// The transaction was accepted and successful. + accepted = 1, + + /// The transaction was rejected because the servers history contained + /// causally unrelated changes. I.e., the requesting client lost a race to + /// be served first. The client should try again. + rejected = 2, + + /// The server did not support serialized transactions at all, or did not + /// support it on the targeted Realm in particular. + not_supported = 3, +}; + + + +/// \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) (obsolete) + 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 + bad_decompression = 110, // Error in decompression (UPLOAD) + bad_changeset_header_syntax = 111, // Bad syntax in a changeset header (UPLOAD) + bad_changeset_size = 112, // Bad size specified in changeset header (UPLOAD) + bad_changesets = 113, // Bad changesets (UPLOAD) + + // 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 (STATE_REQUEST, BIND, REFRESH) + bad_server_file_ident = 207, // Bad server file identifier (IDENT) (obsolete!) + bad_client_file_ident = 208, // Bad client file identifier (IDENT) + bad_server_version = 209, // Bad server version (IDENT, UPLOAD, TRANSACT) + bad_client_version = 210, // Bad client version (IDENT, UPLOAD) + diverging_histories = 211, // Diverging histories (IDENT) + bad_changeset = 212, // Bad changeset (UPLOAD) + superseded = 213, // Superseded by new session for same client-side file (deprecated) + disabled_session = 213, // Alias for `superseded` (deprecated) + partial_sync_disabled = 214, // Partial sync disabled (BIND, STATE_REQUEST) + unsupported_session_feature = 215, // Unsupported session-level feature + bad_origin_file_ident = 216, // Bad origin file identifier (UPLOAD) + bad_client_file = 217, // Synchronization no longer possible for client-side file + server_file_deleted = 218, // Server file was deleted while session was bound to it + client_file_blacklisted = 219, // Client file has been blacklisted (IDENT) + user_blacklisted = 220, // User has been blacklisted (BIND) + transact_before_upload = 221, // Serialized transaction before upload completion + client_file_expired = 222, // Client file has expired +}; + +constexpr bool is_session_level_error(ProtocolError); + +/// 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 { + + + + + +// Implementation + +inline bool is_consistent(DownloadCursor dc) noexcept +{ + return (dc.server_version != 0 || dc.last_integrated_client_version == 0); +} + +inline bool are_mutually_consistent(DownloadCursor a, DownloadCursor b) noexcept +{ + if (a.server_version < b.server_version) + return (a.last_integrated_client_version <= b.last_integrated_client_version); + if (a.server_version > b.server_version) + return (a.last_integrated_client_version >= b.last_integrated_client_version); + return (a.last_integrated_client_version == b.last_integrated_client_version); +} + +inline bool is_consistent(UploadCursor uc) noexcept +{ + return (uc.client_version != 0 || uc.last_integrated_server_version == 0); +} + +inline bool are_mutually_consistent(UploadCursor a, UploadCursor b) noexcept +{ + if (a.client_version < b.client_version) + return (a.last_integrated_server_version <= b.last_integrated_server_version); + if (a.client_version > b.client_version) + return (a.last_integrated_server_version >= b.last_integrated_server_version); + return (a.last_integrated_server_version == b.last_integrated_server_version); +} + +constexpr bool is_session_level_error(ProtocolError error) +{ + return int(error) >= 200 && int(error) <= 299; +} + +} // namespace sync +} // namespace realm + +#endif // REALM_SYNC_PROTOCOL_HPP diff --git a/src/vendor-include/realm-ios/include/realm/sync/transform.hpp b/src/vendor-include/realm-ios/include/realm/sync/transform.hpp new file mode 100644 index 000000000..d11358575 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/sync/transform.hpp @@ -0,0 +1,353 @@ +/************************************************************************* + * + * 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 +#include +#include +#include +#include + +namespace realm { +namespace sync { + +struct Changeset; + +/// Represents an entry in the history of changes in a sync-enabled Realm +/// file. Server and client use different history formats, but this class is +/// used both on the server and the client side. Each history entry corresponds +/// to a version of the Realm state. For server and client-side histories, these +/// versions are referred to as *server versions* and *client versions* +/// respectively. These versions may, or may not correspond to Realm snapshot +/// numbers (on the server-side they currently do not). +/// +/// FIXME: Move this class into a separate header +/// (``). +class HistoryEntry { +public: + /// 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 peer (client or + /// server) identified by `origin_file_ident`. + timestamp_type origin_timestamp; + + /// The identifier of the file in the context of which the initial + /// untransformed changeset originated, or zero if the changeset originated + /// on the local peer (client or server). + /// + /// For example, when a changeset "travels" from a file with identifier 2 on + /// client A, through a file with identifier 1 on the server, to a file with + /// identifier 3 on client B, then `origin_file_ident` will be 0 on client + /// A, 2 on the server, and 2 on client B. On the other hand, if the server + /// was the originator of the changeset, then `origin_file_ident` would be + /// zero on the server, and 1 on both clients. + file_ident_type origin_file_ident; + + /// For changes of local origin on the client side, this is the last server + /// version integrated locally prior to this history entry. In other words, + /// it is a copy of `remote_version` of the last preceding history entry + /// that carries changes of remote origin, or zero if there is no such + /// preceding history entry. + /// + /// For changes of local origin on the server-side, this is always zero. + /// + /// For changes of remote origin, this is the version produced within the + /// remote-side Realm file by the change that gave rise to this history + /// entry. The remote-side Realm file is not always the same Realm file from + /// which the change originated. On the client side, the remote side is + /// always the server side, and `remote_version` is always a server version + /// (since clients do not speak directly to each other). On the server side, + /// the remote side is always a client side, and `remote_version` is always + /// a client version. + version_type remote_version; + + /// Referenced memory is not owned by this class. + ChunkedBinaryData changeset; +}; + + + +/// The interface between the sync history and the operational transformer +/// (Transformer) for the purpose of transforming changesets received from a +/// particular *remote peer*. +class TransformHistory { +public: + /// Get the first history entry where the produced version is greater than + /// `begin_version` and less than or equal to `end_version`, and whose + /// changeset is neither empty, nor produced by integration of a changeset + /// received from the remote peer associated with this history. + /// + /// If \a buffer is non-null, memory will be allocated and transferred to + /// \a buffer. The changeset will be copied into the newly allocated memory. + /// + /// If \a buffer is null, the changeset is not copied out of the Realm, + /// and entry.changeset.data() does not point to the changeset. + /// The changeset in the Realm could be chunked, hence it is not possible + /// to point to it with BinaryData. entry.changeset.size() always gives the + /// size of the changeset. + /// + /// \param begin_version, end_version The range of versions to consider. If + /// `begin_version` is equal to `end_version`, it 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 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. + /// + /// \return The version produced by the changeset of the located history + /// entry, or zero if no history entry exists matching the specified and + /// implied criteria. + virtual version_type find_history_entry(version_type begin_version, version_type end_version, + HistoryEntry& entry) const noexcept = 0; + + /// Get the specified reciprocal changeset. The targeted history entry is + /// the one whose untransformed changeset produced the specified version. + virtual ChunkedBinaryData get_reciprocal_transform(version_type version) const = 0; + + /// Replace the specified reciprocally transformed changeset. The targeted + /// history entry is the one whose untransformed changeset produced the + /// specified version. + /// + /// \param encoded_changeset The new reciprocally transformed changeset. + virtual void set_reciprocal_transform(version_type version, BinaryData encoded_changeset) = 0; + + virtual ~TransformHistory() noexcept {} +}; + + + +class TransformError; // Exception + +class Transformer { +public: + class RemoteChangeset; + class Reporter; + + /// Produce operationally transformed versions of the specified changesets, + /// which are assumed to be received from a particular remote peer, P, + /// represented by the specified transform history. Note that P is not + /// necessarily the peer on which the changes originated. + /// + /// Operational transformation is carried out between the specified + /// changesets 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. + /// + /// 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. + /// + /// \param local_file_ident The identifier of the local Realm file. The + /// transformer uses this as the actual origin file identifier for + /// changesets where HistoryEntry::origin_file_ident is zero, i.e., when the + /// changeset is of local origin. The specified identifier must never be + /// zero. + /// + /// \return The size of the transformed version of the specified + /// changesets. Upon return, the transformed changesets are concatenated + /// and placed in \a output_buffer. + /// + /// \throw TransformError Thrown if operational transformation fails due to + /// a problem with the specified changeset. + /// + /// FIXME: Consider using std::error_code instead of throwing + /// TransformError. + virtual void transform_remote_changesets(TransformHistory&, + file_ident_type local_file_ident, + version_type current_local_version, + Changeset* changesets, + std::size_t num_changesets, + Reporter* = nullptr, + util::Logger* = nullptr) = 0; + + virtual ~Transformer() noexcept {} +}; + +std::unique_ptr make_transformer(); + +} // namespace sync + + +namespace _impl { + +class TransformerImpl : public sync::Transformer { +public: + using Changeset = sync::Changeset; + using file_ident_type = sync::file_ident_type; + using HistoryEntry = sync::HistoryEntry; + using Instruction = sync::Instruction; + using TransformHistory = sync::TransformHistory; + using version_type = sync::version_type; + + TransformerImpl(); + + void transform_remote_changesets(TransformHistory&, file_ident_type, version_type, Changeset*, + std::size_t, Reporter*, util::Logger*) override; + + struct Side; + struct MajorSide; + struct MinorSide; + +protected: + virtual void merge_changesets(file_ident_type local_file_ident, + Changeset* their_changesets, + std::size_t their_size, + // our_changesets is a pointer-pointer because these changesets + // are held by the reciprocal transform cache. + Changeset** our_changesets, + std::size_t our_size, + Reporter* reporter, + util::Logger* logger); + +private: + util::metered::map> m_reciprocal_transform_cache; + + TransactLogParser m_changeset_parser; + + Changeset& get_reciprocal_transform(TransformHistory&, file_ident_type local_file_ident, + version_type version, const HistoryEntry&); + void flush_reciprocal_transform_cache(TransformHistory&); + + static size_t emit_changesets(const Changeset*, + size_t num_changesets, + util::Buffer& output_buffer); + + struct Discriminant; + struct Transformer; + struct MergeTracer; + + template + void merge_instructions(LeftSide&, RightSide&); +}; + +} // namespace _impl + + +namespace sync { + +class Transformer::RemoteChangeset { +public: + /// 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 = 0; + + /// 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 = 0; + + /// The changeset itself. + ChunkedBinaryData data; + + /// Same meaning as `HistoryEntry::origin_timestamp`. + timestamp_type origin_timestamp = 0; + + /// Same meaning as `HistoryEntry::origin_file_ident`. + file_ident_type origin_file_ident = 0; + + /// If the changeset was compacted during download, the size of the original + /// changeset. + std::size_t original_changeset_size = 0; + + RemoteChangeset() {} + RemoteChangeset(version_type rv, version_type lv, ChunkedBinaryData d, timestamp_type ot, + file_ident_type fi); +}; + + + +class Transformer::Reporter { +public: + virtual void on_changesets_merged(long num_merges) = 0; +}; + + + +void parse_remote_changeset(const Transformer::RemoteChangeset&, Changeset&); + + + + +// 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, + ChunkedBinaryData d, timestamp_type ot, + file_ident_type fi): + remote_version(rv), + last_integrated_local_version(lv), + data(d), + origin_timestamp(ot), + origin_file_ident(fi) +{ +} + +} // namespace sync +} // namespace realm + +#endif // REALM_SYNC_TRANSFORM_HPP diff --git a/src/vendor-include/realm-ios/include/realm/sync/version.hpp b/src/vendor-include/realm-ios/include/realm/sync/version.hpp new file mode 100644 index 000000000..becf70ea5 --- /dev/null +++ b/src/vendor-include/realm-ios/include/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 5 +#define REALM_SYNC_VER_MINOR 0 +#define REALM_SYNC_VER_PATCH 3 +#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/src/vendor-include/realm-ios/include/realm/table.hpp b/src/vendor-include/realm-ios/include/realm/table.hpp new file mode 100644 index 000000000..a8440e719 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/table.hpp @@ -0,0 +1,1504 @@ +/************************************************************************* + * + * 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 +#include + +// Only set this to one when testing the code paths that exercise object ID +// hash collisions. It artificially limits the "optimistic" local ID to use +// only the lower 15 bits of the ID rather than the lower 63 bits, making it +// feasible to generate collisions within reasonable time. +#define REALM_EXERCISE_OBJECT_ID_COLLISION 0 + +namespace realm { + +class BacklinkColumn; +template +class BacklinkCount; +class BinaryColumy; +class ConstTableView; +class Group; +class SortDescriptor; +class StringIndex; +class TableView; +template +class Columns; +template +class SubQuery; +struct LinkTargetInfo; +class ColKeys; +struct GlobalKey; +class LinkChain; + +struct Link { +}; +typedef Link BackLink; + + +namespace _impl { +class TableFriend; +} +namespace metrics { +class QueryInfo; +} + +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. + 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. + Table(const Table&, Allocator& = Allocator::get_default()); + + ~Table() noexcept; + + Allocator& get_alloc() const; + + /// Construct a copy of the specified table as a new freestanding top-level + /// table with dynamic lifetime. This method is deprecated. + TableRef copy(Allocator& = Allocator::get_default()) const; + + /// 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(ColKey col_key) const; + + // Whether or not the column is a list. + bool is_list(ColKey col_key) const; + + //@{ + /// Conventience functions for inspecting the dynamic table type. + /// + size_t get_column_count() const noexcept; + DataType get_column_type(ColKey column_key) const; + StringData get_column_name(ColKey column_key) const; + ColumnAttrMask get_column_attr(ColKey column_key) const noexcept; + ColKey get_column_key(StringData name) const noexcept; + ColKeys get_column_keys() const; + typedef util::Optional> BacklinkOrigin; + BacklinkOrigin find_backlink_origin(StringData origin_table_name, StringData origin_col_name) const noexcept; + BacklinkOrigin find_backlink_origin(ColKey backlink_col) const noexcept; + //@} + + // Primary key columns + ColKey get_primary_key_column() const; + void set_primary_key_column(ColKey col); + void validate_primary_column(); + + //@{ + /// Convenience functions for manipulating the dynamic table type. + /// + static const size_t max_column_name_length = 63; + static const uint64_t max_num_columns = 0xFFFFUL; // <-- must be power of two -1 + ColKey add_column(DataType type, StringData name, bool nullable = false); + ColKey add_column_list(DataType type, StringData name, bool nullable = false); + ColKey add_column_link(DataType type, StringData name, Table& target, LinkType link_type = link_Weak); + + // Pass a ColKey() as first argument to have a new colkey generated + // Requesting a specific ColKey may fail with invalidkey exception, if the key is already in use + // We recommend allowing Core to choose the ColKey. + ColKey insert_column(ColKey col_key, DataType type, StringData name, bool nullable = false); + ColKey insert_column_link(ColKey col_key, DataType type, StringData name, Table& target, + LinkType link_type = link_Weak); + void remove_column(ColKey col_key); + void rename_column(ColKey col_key, StringData new_name); + bool valid_column(ColKey col_key) const noexcept; + void check_column(ColKey col_key) const; + //@} + + /// There are two kinds of links, 'weak' and 'strong'. A strong link is one + /// that implies ownership, i.e., that the origin object (parent) owns the + /// target parent (child). Simply stated, this means that when the origin object + /// (parent) is removed, so is the target object (child). If there are multiple + /// strong links to an object, the origin objects share ownership, and the + /// target object is removed when the last owner disappears. Weak links do not + /// imply ownership, and will be nullified or removed when the target object + /// disappears. + /// + /// To put this in precise terms; when a strong link is broken, and the + /// target object has no other strong links to it, the target object is removed. A + /// object 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 + /// + /// - the origin object is explicitly removed + /// + /// - the origin object is cascade-removed, or if + /// + /// - the origin field 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 object. I.e., no objects will be cascade-removed + /// due to such an operation. + /// + /// When a object 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 object are nullified. For link + /// list columns (type_LinkList), links to the removed object are removed from + /// the list. + /// + /// When a object 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 object to be cascade-removed as a consequence of being + /// modified. This happens, for example, if two objects, 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. + /// + /// The link type must be specified at column creation and cannot be changed + // Returns the link type for the given column. + // Throws an LogicError if target column is not a link column. + LinkType get_link_type(ColKey col_key) const; + + /// True for `col_type_Link` and `col_type_LinkList`. + static bool is_link_type(ColumnType) noexcept; + + //@{ + + /// 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 the + /// 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 the 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. + /// + /// \param col_key The key of a column of the table. + + bool has_search_index(ColKey col_key) const noexcept; + void add_search_index(ColKey col_key); + void remove_search_index(ColKey col_key); + + void enumerate_string_column(ColKey col_key); + bool is_enumerated(ColKey col_key) const noexcept; + bool contains_unique_values(ColKey col_key) const; + + //@} + + /// 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(ColKey col_key) const; + + template + Columns column(ColKey col_key) const; // FIXME: Should this one have been declared noexcept? + template + Columns column(const Table& origin, ColKey origin_col_key) const; + + // BacklinkCount is a total count per row and therefore not attached to a specific column + template + BacklinkCount get_backlink_count() const; + + template + SubQuery column(ColKey col_key, Query subquery) const; + template + SubQuery column(const Table& origin, ColKey origin_col_key, Query subquery) const; + + // Table size and deletion + bool is_empty() const noexcept; + size_t size() const noexcept; + + //@{ + + /// Object handling. + + // Create an object with key. If the key is omitted, a key will be generated by the system + Obj create_object(ObjKey key = {}, const FieldValues& = {}); + // Create an object with specific GlobalKey. + Obj create_object(GlobalKey object_id, const FieldValues& = {}); + // Create an object with primary key + Obj create_object_with_primary_key(const Mixed& primary_key); + /// Create a number of objects and add corresponding keys to a vector + void create_objects(size_t number, std::vector& keys); + /// Create a number of objects with keys supplied + void create_objects(const std::vector& keys); + /// Does the key refer to an object within the table? + bool is_valid(ObjKey key) const + { + return m_clusters.is_valid(key); + } + ObjKey get_obj_key(GlobalKey id) const; + GlobalKey get_object_id(ObjKey key) const; + Obj get_object(ObjKey key) + { + return m_clusters.get(key); + } + ConstObj get_object(ObjKey key) const + { + return m_clusters.get(key); + } + Obj get_object(size_t ndx) + { + return m_clusters.get(ndx); + } + ConstObj get_object(size_t ndx) const + { + return m_clusters.get(ndx); + } + // Get logical index for object. This function is not very efficient + size_t get_object_ndx(ObjKey key) const + { + return m_clusters.get_ndx(key); + } + + void dump_objects(); + + bool traverse_clusters(ClusterTree::TraverseFunction func) const + { + return m_clusters.traverse(func); + } + + /// remove_object() removes the specified object from the table. + /// The removal of an object a table may cause other linked objects to be + /// cascade-removed. The clearing of a table may also cause linked objects + /// to be cascade-removed, but in this respect, the effect is exactly as if + /// each object had been removed individually. See set_link_type() for details. + void remove_object(ObjKey key); + /// remove_object_recursive() will delete linked rows if the removed link was the + /// last one holding on to the row in question. This will be done recursively. + void remove_object_recursive(ObjKey key); + void clear(); + using Iterator = ClusterTree::Iterator; + using ConstIterator = ClusterTree::ConstIterator; + ConstIterator begin() const; + ConstIterator end() const; + Iterator begin(); + Iterator end(); + void remove_object(const ConstIterator& it) + { + remove_object(it->get_key()); + } + //@} + + + TableRef get_link_target(ColKey column_key) noexcept; + ConstTableRef get_link_target(ColKey column_key) const noexcept; + + 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(); + + /// Only group-level unordered tables can be used as origins or targets of + /// links. + bool is_group_level() const noexcept; + + /// A Table accessor obtained from a frozen transaction is also frozen. + bool is_frozen() const noexcept { return m_is_frozen; } + + /// 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; + TableKey get_key() const noexcept; + + uint64_t allocate_sequence_number(); + // Used by upgrade + void set_sequence_number(uint64_t seq); + void set_collision_map(ref_type ref); + + // Get the key of this table directly, without needing a Table accessor. + static TableKey get_key_direct(Allocator& alloc, ref_type top_ref); + + // Aggregate functions + size_t count_int(ColKey col_key, int64_t value) const; + size_t count_string(ColKey col_key, StringData value) const; + size_t count_float(ColKey col_key, float value) const; + size_t count_double(ColKey col_key, double value) const; + + int64_t sum_int(ColKey col_key) const; + double sum_float(ColKey col_key) const; + double sum_double(ColKey col_key) const; + int64_t maximum_int(ColKey col_key, ObjKey* return_ndx = nullptr) const; + float maximum_float(ColKey col_key, ObjKey* return_ndx = nullptr) const; + double maximum_double(ColKey col_key, ObjKey* return_ndx = nullptr) const; + Timestamp maximum_timestamp(ColKey col_key, ObjKey* return_ndx = nullptr) const; + int64_t minimum_int(ColKey col_key, ObjKey* return_ndx = nullptr) const; + float minimum_float(ColKey col_key, ObjKey* return_ndx = nullptr) const; + double minimum_double(ColKey col_key, ObjKey* return_ndx = nullptr) const; + Timestamp minimum_timestamp(ColKey col_key, ObjKey* return_ndx = nullptr) const; + double average_int(ColKey col_key, size_t* value_count = nullptr) const; + double average_float(ColKey col_key, size_t* value_count = nullptr) const; + double average_double(ColKey col_key, size_t* value_count = nullptr) const; + + // Will return pointer to search index accessor. Will return nullptr if no index + StringIndex* get_search_index(ColKey col) const noexcept + { + report_invalid_key(col); + if (!has_search_index(col)) + return nullptr; + return m_index_accessors[col.get_index().val]; + } + template + ObjKey find_first(ColKey col_key, T value) const; + + ObjKey find_first_int(ColKey col_key, int64_t value) const; + ObjKey find_first_bool(ColKey col_key, bool value) const; + ObjKey find_first_timestamp(ColKey col_key, Timestamp value) const; + ObjKey find_first_float(ColKey col_key, float value) const; + ObjKey find_first_double(ColKey col_key, double value) const; + ObjKey find_first_string(ColKey col_key, StringData value) const; + ObjKey find_first_binary(ColKey col_key, BinaryData value) const; + ObjKey find_first_null(ColKey col_key) const; + + // TableView find_all_link(Key target_key); + // ConstTableView find_all_link(Key target_key) const; + TableView find_all_int(ColKey col_key, int64_t value); + ConstTableView find_all_int(ColKey col_key, int64_t value) const; + TableView find_all_bool(ColKey col_key, bool value); + ConstTableView find_all_bool(ColKey col_key, bool value) const; + TableView find_all_float(ColKey col_key, float value); + ConstTableView find_all_float(ColKey col_key, float value) const; + TableView find_all_double(ColKey col_key, double value); + ConstTableView find_all_double(ColKey col_key, double value) const; + TableView find_all_string(ColKey col_key, StringData value); + ConstTableView find_all_string(ColKey col_key, StringData value) const; + TableView find_all_binary(ColKey col_key, BinaryData value); + ConstTableView find_all_binary(ColKey col_key, BinaryData value) const; + TableView find_all_null(ColKey col_key); + ConstTableView find_all_null(ColKey col_key) const; + + /// The following column types are supported: String, Integer, OldDateTime, Bool + TableView get_distinct_view(ColKey col_key); + ConstTableView get_distinct_view(ColKey col_key) const; + + TableView get_sorted_view(ColKey col_key, bool ascending = true); + ConstTableView get_sorted_view(ColKey col_key, bool ascending = true) const; + + TableView get_sorted_view(SortDescriptor order); + ConstTableView get_sorted_view(SortDescriptor order) const; + + // Report the current content version. This is a 64-bit value which is bumped whenever + // the content in the table changes. + uint_fast64_t get_content_version() const noexcept; + + // Report the current instance version. This is a 64-bit value which is bumped + // whenever the table accessor is recycled. + uint_fast64_t get_instance_version() const noexcept; + + // Report the current storage version. This is a 64-bit value which is bumped + // whenever the location in memory of any part of the table changes. + uint_fast64_t get_storage_version(uint64_t instance_version) const; + uint_fast64_t get_storage_version() const; + void bump_storage_version() const noexcept; + void bump_content_version() const noexcept; + + // Change the nullability of the column identified by col_key. + // This might result in the creation of a new column and deletion of the old. + // The column key to use going forward is returned. + // If the conversion is from nullable to non-nullable, throw_on_null determines + // the reaction to encountering a null value: If clear, null values will be + // converted to default values. If set, a 'column_not_nullable' is thrown and the + // table is unchanged. + ColKey set_nullability(ColKey col_key, bool nullable, bool throw_on_null); + + // Iterate through (subset of) columns. The supplied function may abort iteration + // by returning 'true' (early out). + template + bool for_each_and_every_column(Func func) const + { + for (auto col_key : m_leaf_ndx2colkey) { + if (!col_key) + continue; + if (func(col_key)) + return true; + } + return false; + } + template + bool for_each_public_column(Func func) const + { + for (auto col_key : m_leaf_ndx2colkey) { + if (!col_key) + continue; + if (col_key.get_type() == col_type_BackLink) + continue; + if (func(col_key)) + return true; + } + return false; + } + template + bool for_each_backlink_column(Func func) const + { + // FIXME: Optimize later - to not iterate through all non-backlink columns: + for (auto col_key : m_leaf_ndx2colkey) { + if (!col_key) + continue; + if (col_key.get_type() != col_type_BackLink) + continue; + if (func(col_key)) + return true; + } + return false; + } + +private: + template + TableView find_all(ColKey col_key, T value); + void build_column_mapping(); + ColKey generate_col_key(ColumnType ct, ColumnAttrMask attrs); + void convert_column(ColKey from, ColKey to, bool throw_on_null); + template + void change_nullability(ColKey from, ColKey to, bool throw_on_null); + template + void change_nullability_list(ColKey from, ColKey to, bool throw_on_null); + +public: + // mapping between index used in leaf nodes (leaf_ndx) and index used in spec (spec_ndx) + // as well as the full column key. A leaf_ndx can be obtained directly from the column key + size_t colkey2spec_ndx(ColKey key) const; + size_t leaf_ndx2spec_ndx(ColKey::Idx idx) const; + ColKey::Idx spec_ndx2leaf_ndx(size_t idx) const; + ColKey leaf_ndx2colkey(ColKey::Idx idx) const; + ColKey spec_ndx2colkey(size_t ndx) const; + void report_invalid_key(ColKey col_key) const; + size_t num_leaf_cols() const; + //@{ + /// 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<(). + /// + /// FIXME: Deprecate or change to return ObjKey. + size_t lower_bound_int(ColKey col_key, int64_t value) const noexcept; + size_t upper_bound_int(ColKey col_key, int64_t value) const noexcept; + size_t lower_bound_bool(ColKey col_key, bool value) const noexcept; + size_t upper_bound_bool(ColKey col_key, bool value) const noexcept; + size_t lower_bound_float(ColKey col_key, float value) const noexcept; + size_t upper_bound_float(ColKey col_key, float value) const noexcept; + size_t lower_bound_double(ColKey col_key, double value) const noexcept; + size_t upper_bound_double(ColKey col_key, double value) const noexcept; + size_t lower_bound_string(ColKey col_key, StringData value) const noexcept; + size_t upper_bound_string(ColKey col_key, 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(ConstTableView* tv = nullptr) + { + return Query(m_own_ref, tv); + } + + // FIXME: We need a ConstQuery class or runtime check against modifications in read transaction. + Query where(ConstTableView* tv = nullptr) const + { + return Query(m_own_ref, tv); + } + + // Perform queries on a LinkView. The returned Query holds a reference to list. + Query where(const LnkLst& list) const + { + return Query(m_own_ref, list); + } + + //@{ + /// WARNING: The link() and backlink() methods will alter a state on the Table object and return a reference + /// to itself. Be aware if assigning the return value of link() to a variable; this might be an error! + + /// This is an error: + + /// Table& cats = owners->link(1); + /// auto& dogs = owners->link(2); + + /// Query q = person_table->where() + /// .and_query(cats.column(5).equal("Fido")) + /// .Or() + /// .and_query(dogs.column(6).equal("Meowth")); + + /// Instead, do this: + + /// Query q = owners->where() + /// .and_query(person_table->link(1).column(5).equal("Fido")) + /// .Or() + /// .and_query(person_table->link(2).column(6).equal("Meowth")); + + /// The two calls to link() in the erroneous example will append the two values 0 and 1 to an internal vector in + /// the owners table, and we end up with three references to that same table: owners, cats and dogs. They are all + /// the same table, its vector has the values {0, 1}, so a query would not make any sense. + LinkChain link(ColKey link_column) const; + LinkChain backlink(const Table& origin, ColKey origin_col_key) const; + + // Conversion + void to_json(std::ostream& out, size_t link_depth = 0, + std::map* renames = nullptr) const; + + /// \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; + + /// Compute the sum of the sizes in number of bytes of all the array nodes + /// that currently make up this table. See also + /// Group::compute_aggregate_byte_size(). + /// + /// If this table accessor is the detached state, this function returns + /// zero. + size_t compute_aggregated_byte_size() const noexcept; + + // Debug + void verify() const; + +#ifdef REALM_DEBUG + MemStats stats() const; +#endif + TableRef get_opposite_table(ColKey col_key) const; + TableKey get_opposite_table_key(ColKey col_key) const; + bool links_to_self(ColKey col_key) const; + ColKey get_opposite_column(ColKey col_key) const; + ColKey find_opposite_column(ColKey col_key) const; + +protected: + /// Compare the objects 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_objects(const Table&) const; + + void check_lists_are_empty(size_t row_ndx) const; + +private: + mutable WrappedAllocator m_alloc; + Array m_top; + void update_allocator_wrapper(bool writable) + { + m_alloc.update_from_underlying_allocator(writable); + } + Spec m_spec; // 1st slot in m_top + ClusterTree m_clusters; // 3rd slot in m_top + TableKey m_key; // 4th slot in m_top + Array m_index_refs; // 5th slot in m_top + Array m_opposite_table; // 7th slot in m_top + Array m_opposite_column; // 8th slot in m_top + std::vector m_index_accessors; + ColKey m_primary_key_col; + Replication* const* m_repl; + static Replication* g_dummy_replication; + bool m_is_frozen = false; + TableRef m_own_ref; + + void batch_erase_rows(const KeyColumn& keys); + size_t do_set_link(ColKey col_key, size_t row_ndx, size_t target_row_ndx); + + void populate_search_index(ColKey col_key); + + // Migration support + void migrate_column_info(util::FunctionRef); + void migrate_indexes(util::FunctionRef); + void migrate_subspec(util::FunctionRef); + void convert_links_from_ndx_to_key(util::FunctionRef); + ref_type get_oid_column_ref() const; + void create_columns(util::FunctionRef); + void migrate_objects(util::FunctionRef); + void migrate_links(util::FunctionRef); + void finalize_migration(); + + /// 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; + + /// Create an uninitialized accessor whose lifetime is managed by Group + Table(Replication* const* repl, Allocator&); + void revive(Replication* const* repl, Allocator& new_allocator, bool writable); + + void init(ref_type top_ref, ArrayParent*, size_t ndx_in_parent, bool is_writable, bool is_frozen); + + void set_key(TableKey key); + + ColKey do_insert_column(ColKey col_key, DataType type, StringData name, LinkTargetInfo& link_target_info, + bool nullable = false, bool listtype = false, LinkType link_type = link_Weak); + + struct InsertSubtableColumns; + struct EraseSubtableColumns; + struct RenameSubtableColumns; + + ColKey insert_root_column(ColKey col_key, DataType type, StringData name, LinkTargetInfo& link_target, + bool nullable = false, bool linktype = false, LinkType link_type = link_Weak); + void erase_root_column(ColKey col_key); + ColKey do_insert_root_column(ColKey col_key, ColumnType, StringData name, bool nullable = false, + bool listtype = false, LinkType link_type = link_Weak); + void do_erase_root_column(ColKey col_key); + ColKey insert_backlink_column(TableKey origin_table_key, ColKey origin_col_key, ColKey backlink_col_key); + void erase_backlink_column(ColKey backlink_col_key); + + void set_opposite_column(ColKey col_key, TableKey opposite_table, ColKey opposite_column); + void do_set_primary_key_column(ColKey col_key); + void validate_column_is_unique(ColKey col_key) const; + void rebuild_table_with_pk_column(); + + ObjKey get_next_key(); + /// Some Object IDs are generated as a tuple of the client_file_ident and a + /// local sequence number. This function takes the next number in the + /// sequence for the given table and returns an appropriate globally unique + /// GlobalKey. + GlobalKey allocate_object_id_squeezed(); + + /// Find the local 64-bit object ID for the provided global 128-bit ID. + ObjKey global_to_local_object_id_hashed(GlobalKey global_id) const; + + /// After a local ObjKey collision has been detected, this function may be + /// called to obtain a non-colliding local ObjKey in such a way that subsequent + /// calls to global_to_local_object_id() will return the correct local ObjKey + /// for both \a incoming_id and \a colliding_id. + ObjKey allocate_local_id_after_hash_collision(GlobalKey incoming_id, GlobalKey colliding_id, + ObjKey colliding_local_id); + /// Should be called when an object is deleted + void free_local_id_after_hash_collision(ObjKey key); + + /// 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; + + // Detach accessor. This recycles the Table accessor and all subordinate + // accessors become invalid. + void detach() noexcept; + void fully_detach() noexcept; + + ColumnType get_real_column_type(ColKey col_key) 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; + uint64_t get_sync_file_id() const noexcept; + + 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; + + /// Create an empty table with independent spec and return just + /// the reference to the underlying memory. + static ref_type create_empty_table(Allocator&, TableKey = TableKey()); + + void nullify_links(CascadeState&); + void remove_recursive(CascadeState&); + //@{ + + /// Cascading removal of strong links. + /// + /// FIXME: Update this explanation + /// + /// 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, CascadeState&) + { + REALM_ASSERT(false); // unimplemented + } + + void cascade_break_backlinks_to_all_rows(CascadeState&) + { + REALM_ASSERT(false); // unimplemented + } + + void remove_backlink_broken_rows(const CascadeState&) + { + REALM_ASSERT(false); // unimplemented + } + + //@} + + /// Used by query. Follows chain of link columns and returns final target table + const Table* get_link_chain_target(const std::vector&) const; + + Replication* get_repl() const 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. + void refresh_accessor_tree(); + void refresh_index_accessors(); + void refresh_content_version(); + void flush_for_commit(); + + bool is_cross_table_link_target() const noexcept; + template + R aggregate(ColKey col_key, T value = {}, size_t* resultcount = nullptr, ObjKey* return_ndx = nullptr) const; + template + double average(ColKey col_key, size_t* resultcount) const; + + std::vector m_leaf_ndx2colkey; + std::vector m_spec_ndx2leaf_ndx; + std::vector m_leaf_ndx2spec_ndx; + + uint64_t m_in_file_version_at_transaction_boundary = 0; + + static constexpr int top_position_for_spec = 0; + static constexpr int top_position_for_columns = 1; + static constexpr int top_position_for_cluster_tree = 2; + static constexpr int top_position_for_key = 3; + static constexpr int top_position_for_search_indexes = 4; + static constexpr int top_position_for_column_key = 5; + static constexpr int top_position_for_version = 6; + static constexpr int top_position_for_opposite_table = 7; + static constexpr int top_position_for_opposite_column = 8; + static constexpr int top_position_for_sequence_number = 9; + static constexpr int top_position_for_collision_map = 10; + static constexpr int top_position_for_pk_col = 11; + static constexpr int top_array_size = 12; + + enum { s_collision_map_lo = 0, s_collision_map_hi = 1, s_collision_map_local_id = 2, s_collision_map_num_slots }; + + friend class SubtableNode; + friend class _impl::TableFriend; + friend class Query; + friend class metrics::QueryInfo; + template + friend class SimpleQuerySupport; + friend class LangBindHelper; + friend class ConstTableView; + template + friend class Columns; + friend class Columns; + friend class ParentNode; + friend struct util::serializer::SerialisationState; + friend class LinksToNode; + friend class LinkMap; + friend class LinkView; + friend class Group; + friend class Transaction; + friend class Cluster; + friend class ClusterTree; + friend class ColKeyIterator; + friend class ConstObj; + friend class Obj; + friend class IncludeDescriptor; +}; + +class ColKeyIterator { +public: + bool operator!=(const ColKeyIterator& other) + { + return m_pos != other.m_pos; + } + ColKeyIterator& operator++() + { + ++m_pos; + return *this; + } + ColKeyIterator operator++(int) + { + ColKeyIterator tmp(m_table, m_pos); + ++m_pos; + return tmp; + } + ColKey operator*() + { + if (m_pos < m_table->get_column_count()) { + REALM_ASSERT(m_table->m_spec.get_key(m_pos) == m_table->spec_ndx2colkey(m_pos)); + return m_table->m_spec.get_key(m_pos); + } + return {}; + } + +private: + friend class ColKeys; + const Table* m_table; + size_t m_pos; + + ColKeyIterator(const Table* t, size_t p) + : m_table(t) + , m_pos(p) + { + } +}; + +class ColKeys { +public: + ColKeys(const Table* t) + : m_table(t) + { + } + + ColKeys() + : m_table(nullptr) + { + } + + size_t size() const + { + return m_table->get_column_count(); + } + bool empty() const + { + return size() == 0; + } + ColKey operator[](size_t p) const + { + return ColKeyIterator(m_table, p).operator*(); + } + ColKeyIterator begin() const + { + return ColKeyIterator(m_table, 0); + } + ColKeyIterator end() const + { + return ColKeyIterator(m_table, size()); + } + +private: + const Table* m_table; +}; + +// Class used to collect a chain of links when building up a Query following links. +// It has member functions corresponding to the ones defined on Table. +class LinkChain { +public: + LinkChain(ConstTableRef t) + : m_current_table(t.unchecked_ptr()) + , m_base_table(t) + { + } + const Table* get_base_table() + { + return m_base_table.unchecked_ptr(); + } + + LinkChain& link(ColKey link_column) + { + add(link_column); + return *this; + } + + LinkChain& backlink(const Table& origin, ColKey origin_col_key) + { + auto backlink_col_key = origin.get_opposite_column(origin_col_key); + return link(backlink_col_key); + } + + + template + inline Columns column(ColKey col_key) + { + m_current_table->report_invalid_key(col_key); + + // Check if user-given template type equals Realm type. + auto ct = col_key.get_type(); + if (ct == col_type_LinkList) + ct = col_type_Link; + if (ct != ColumnTypeTraits::column_id) + throw LogicError(LogicError::type_mismatch); + + if (std::is_same::value || std::is_same::value || std::is_same::value) { + m_link_cols.push_back(col_key); + } + + return Columns(col_key, m_base_table, m_link_cols); + } + template + Columns column(const Table& origin, ColKey origin_col_key) + { + static_assert(std::is_same::value, ""); + + auto backlink_col_key = origin.get_opposite_column(origin_col_key); + m_link_cols.push_back(backlink_col_key); + + return Columns(backlink_col_key, m_base_table, std::move(m_link_cols)); + } + template + SubQuery column(ColKey col_key, Query subquery) + { + static_assert(std::is_same::value, "A subquery must involve a link list or backlink column"); + return SubQuery(column(col_key), std::move(subquery)); + } + + template + SubQuery column(const Table& origin, ColKey origin_col_key, Query subquery) + { + static_assert(std::is_same::value, "A subquery must involve a link list or backlink column"); + return SubQuery(column(origin, origin_col_key), std::move(subquery)); + } + + + template + BacklinkCount get_backlink_count() + { + return BacklinkCount(m_base_table, std::move(m_link_cols)); + } + +private: + friend class Table; + + std::vector m_link_cols; + const Table* m_current_table; + ConstTableRef m_base_table; + + void add(ColKey ck) + { + // Link column can be a single Link, LinkList, or BackLink. + REALM_ASSERT(m_current_table->valid_column(ck)); + ColumnType type = ck.get_type(); + if (type == col_type_LinkList || type == col_type_Link || type == col_type_BackLink) { + m_current_table = m_current_table->get_opposite_table(ck).unchecked_ptr(); + } + else { + // Only last column in link chain is allowed to be non-link + throw(LogicError::type_mismatch); + } + m_link_cols.push_back(ck); + } +}; + +// Implementation: + +inline ColKeys Table::get_column_keys() const +{ + return ColKeys(this); +} + +inline uint_fast64_t Table::get_content_version() const noexcept +{ + return m_alloc.get_content_version(); +} + +inline uint_fast64_t Table::get_instance_version() const noexcept +{ + return m_alloc.get_instance_version(); +} + + +inline uint_fast64_t Table::get_storage_version(uint64_t instance_version) const +{ + return m_alloc.get_storage_version(instance_version); +} + +inline uint_fast64_t Table::get_storage_version() const +{ + return m_alloc.get_storage_version(); +} + + +inline TableKey Table::get_key() const noexcept +{ + return m_key; +} + +inline void Table::bump_storage_version() const noexcept +{ + return m_alloc.bump_storage_version(); +} + +inline void Table::bump_content_version() const noexcept +{ + m_alloc.bump_content_version(); +} + + + +inline size_t Table::get_column_count() const noexcept +{ + return m_spec.get_public_column_count(); +} + +inline StringData Table::get_column_name(ColKey column_key) const +{ + auto spec_ndx = colkey2spec_ndx(column_key); + REALM_ASSERT_3(spec_ndx, <, get_column_count()); + return m_spec.get_column_name(spec_ndx); +} + +inline ColKey Table::get_column_key(StringData name) const noexcept +{ + size_t spec_ndx = m_spec.get_column_index(name); + if (spec_ndx == npos) + return ColKey(); + return spec_ndx2colkey(spec_ndx); +} + +inline ColumnType Table::get_real_column_type(ColKey col_key) const noexcept +{ + return col_key.get_type(); +} + +inline DataType Table::get_column_type(ColKey column_key) const +{ + return DataType(column_key.get_type()); +} + +inline ColumnAttrMask Table::get_column_attr(ColKey column_key) const noexcept +{ + return column_key.get_attrs(); +} + + +inline Table::Table(Allocator& alloc) + : m_alloc(alloc) + , m_top(m_alloc) + , m_spec(m_alloc) + , m_clusters(this, m_alloc) + , m_index_refs(m_alloc) + , m_opposite_table(m_alloc) + , m_opposite_column(m_alloc) + , m_repl(&g_dummy_replication) + , m_own_ref(this, alloc.get_instance_version()) +{ + m_spec.set_parent(&m_top, top_position_for_spec); + m_index_refs.set_parent(&m_top, top_position_for_search_indexes); + m_opposite_table.set_parent(&m_top, top_position_for_opposite_table); + m_opposite_column.set_parent(&m_top, top_position_for_opposite_column); + + ref_type ref = create_empty_table(m_alloc); // Throws + ArrayParent* parent = nullptr; + size_t ndx_in_parent = 0; + init(ref, parent, ndx_in_parent, true, false); +} + +inline Table::Table(Replication* const* repl, Allocator& alloc) + : m_alloc(alloc) + , m_top(m_alloc) + , m_spec(m_alloc) + , m_clusters(this, m_alloc) + , m_index_refs(m_alloc) + , m_opposite_table(m_alloc) + , m_opposite_column(m_alloc) + , m_repl(repl) + , m_own_ref(this, alloc.get_instance_version()) +{ + m_spec.set_parent(&m_top, top_position_for_spec); + m_index_refs.set_parent(&m_top, top_position_for_search_indexes); + m_opposite_table.set_parent(&m_top, top_position_for_opposite_table); + m_opposite_column.set_parent(&m_top, top_position_for_opposite_column); +} + +inline void Table::revive(Replication* const* repl, Allocator& alloc, bool writable) +{ + m_alloc.switch_underlying_allocator(alloc); + m_alloc.update_from_underlying_allocator(writable); + m_repl = repl; + m_own_ref = TableRef(this, m_alloc.get_instance_version()); + + // since we're rebinding to a new table, we'll bump version counters + // FIXME + // this can be optimized if version counters are saved along with the + // table data. + bump_content_version(); + bump_storage_version(); + // we assume all other accessors are detached, so we're done. +} + +inline Allocator& Table::get_alloc() const +{ + return m_alloc; +} + +// For use by queries +template +inline Columns Table::column(ColKey col_key) const +{ + LinkChain lc(m_own_ref); + return lc.column(col_key); +} + +template +inline Columns Table::column(const Table& origin, ColKey origin_col_key) const +{ + LinkChain lc(m_own_ref); + return lc.column(origin, origin_col_key); +} + +template +inline BacklinkCount Table::get_backlink_count() const +{ + return BacklinkCount(this, {}); +} + +template +SubQuery Table::column(ColKey col_key, Query subquery) const +{ + LinkChain lc(m_own_ref); + return lc.column(col_key, subquery); +} + +template +SubQuery Table::column(const Table& origin, ColKey origin_col_key, Query subquery) const +{ + LinkChain lc(m_own_ref); + return lc.column(origin, origin_col_key, subquery); +} + +inline LinkChain Table::link(ColKey link_column) const +{ + LinkChain lc(m_own_ref); + lc.add(link_column); + + return lc; +} + +inline LinkChain Table::backlink(const Table& origin, ColKey origin_col_key) const +{ + auto backlink_col_key = origin.get_opposite_column(origin_col_key); + return link(backlink_col_key); +} + +inline bool Table::is_empty() const noexcept +{ + return size() == 0; +} + +inline size_t Table::size() const noexcept +{ + return m_clusters.size(); +} + + +inline ConstTableRef Table::get_link_target(ColKey col_key) const noexcept +{ + return const_cast(this)->get_link_target(col_key); +} + +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_objects(t); // Throws +} + +inline bool Table::operator!=(const Table& t) const +{ + return !(*this == t); // Throws +} + +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 bool Table::is_link_type(ColumnType col_type) noexcept +{ + return col_type == col_type_Link || col_type == col_type_LinkList; +} + +inline Replication* Table::get_repl() const noexcept +{ + return *m_repl; +} + +inline void Table::set_ndx_in_parent(size_t ndx_in_parent) noexcept +{ + REALM_ASSERT(m_top.is_attached()); + m_top.set_ndx_in_parent(ndx_in_parent); +} + +inline size_t Table::colkey2spec_ndx(ColKey key) const +{ + auto leaf_idx = key.get_index(); + REALM_ASSERT(leaf_idx.val < m_leaf_ndx2spec_ndx.size()); + return m_leaf_ndx2spec_ndx[leaf_idx.val]; +} + +inline size_t Table::num_leaf_cols() const +{ + return m_leaf_ndx2spec_ndx.size(); +} + +inline ColKey Table::spec_ndx2colkey(size_t spec_ndx) const +{ + REALM_ASSERT(spec_ndx < m_spec_ndx2leaf_ndx.size()); + return m_leaf_ndx2colkey[m_spec_ndx2leaf_ndx[spec_ndx].val]; +} + +inline void Table::report_invalid_key(ColKey col_key) const +{ + if (col_key == ColKey()) + throw LogicError(LogicError::column_does_not_exist); + auto idx = col_key.get_index(); + if (idx.val >= m_leaf_ndx2colkey.size() || m_leaf_ndx2colkey[idx.val] != col_key) + throw LogicError(LogicError::column_does_not_exist); +} + +inline size_t Table::leaf_ndx2spec_ndx(ColKey::Idx leaf_ndx) const +{ + REALM_ASSERT(leaf_ndx.val < m_leaf_ndx2colkey.size()); + return m_leaf_ndx2spec_ndx[leaf_ndx.val]; +} + +inline ColKey::Idx Table::spec_ndx2leaf_ndx(size_t spec_ndx) const +{ + REALM_ASSERT(spec_ndx < m_spec_ndx2leaf_ndx.size()); + return m_spec_ndx2leaf_ndx[spec_ndx]; +} + +inline ColKey Table::leaf_ndx2colkey(ColKey::Idx leaf_ndx) const +{ + // this may be called with leaf indicies outside of the table. This can happen + // when a column is removed from the mapping, but space for it is still reserved + // at leaf level. Operations on Cluster and ClusterTree which walks the columns + // based on leaf indicies may ask for colkeys which are no longer valid. + if (leaf_ndx.val < m_leaf_ndx2spec_ndx.size()) + return m_leaf_ndx2colkey[leaf_ndx.val]; + else + return ColKey(); +} + +bool inline Table::valid_column(ColKey col_key) const noexcept +{ + if (col_key == ColKey()) + return false; + ColKey::Idx leaf_idx = col_key.get_index(); + if (leaf_idx.val >= m_leaf_ndx2colkey.size()) + return false; + return col_key == m_leaf_ndx2colkey[leaf_idx.val]; +} + +inline void Table::check_column(ColKey col_key) const +{ + if (REALM_UNLIKELY(!valid_column(col_key))) + throw InvalidKey("No such column"); +} + +// 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, ColKey backlink_key = ColKey()) + : m_target_table(target) + , m_backlink_col_key(backlink_key) + { + if (backlink_key) + m_target_table->report_invalid_key(backlink_key); + } + bool is_valid() const + { + return (m_target_table != nullptr); + } + Table* m_target_table; + ColKey m_backlink_col_key; // a value of ColKey() 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: + 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 TableRef get_opposite_link_table(const Table& table, ColKey col_key); + + static Group* get_parent_group(const Table& table) noexcept + { + return table.get_parent_group(); + } + + static void remove_recursive(Table& table, CascadeState& rows) + { + table.remove_recursive(rows); // Throws + } + + static void batch_erase_rows(Table& table, const KeyColumn& keys) + { + table.batch_erase_rows(keys); // Throws + } +}; + +} // namespace realm + +#endif // REALM_TABLE_HPP diff --git a/src/vendor-include/realm-ios/include/realm/table_ref.hpp b/src/vendor-include/realm-ios/include/realm/table_ref.hpp new file mode 100644 index 000000000..b745d9085 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/table_ref.hpp @@ -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. + * + **************************************************************************/ + +#ifndef REALM_TABLE_REF_HPP +#define REALM_TABLE_REF_HPP + +#include +#include +namespace realm { + + +class Table; +class TableRef; + +class ConstTableRef { +public: + ConstTableRef() noexcept {} + ConstTableRef(std::nullptr_t) noexcept {} + ConstTableRef(const TableRef& other) noexcept; + + const Table* operator->() const; + const Table& operator*() const; + operator bool() const noexcept; + const Table* unchecked_ptr() const + { + return m_table; + } + + bool operator==(const ConstTableRef& other) const + { + return m_table == other.m_table && m_instance_version == other.m_instance_version; + } + + bool operator!=(const ConstTableRef& other) const + { + return !(*this == other); + } + + std::ostream& print(std::ostream& o) const + { + return o << "TableRef(" << m_table << ", " << m_instance_version << ")"; + } + TableRef cast_away_const() const; + static ConstTableRef unsafe_create(const Table* t_ptr); + void check() const; + +protected: + explicit ConstTableRef(const Table* t_ptr, uint64_t instance_version) + : m_table(const_cast(t_ptr)) + , m_instance_version(instance_version) + { + } + friend class Group; + friend class Table; + friend class ClusterTree; + + Table* m_table = nullptr; + uint64_t m_instance_version = 0; +}; + +class TableRef : public ConstTableRef { +public: + TableRef() noexcept + : ConstTableRef() + { + } + TableRef(std::nullptr_t) noexcept + : ConstTableRef() + { + } + + Table* operator->() const; + Table& operator*() const; + Table* unchecked_ptr() const + { + return m_table; + } + static TableRef unsafe_create(Table* t_ptr); + +private: + explicit TableRef(Table* t_ptr, uint64_t instance_version) + : ConstTableRef(t_ptr, instance_version) + { + } + friend class Group; + friend class Table; + friend class ClusterTree; + friend class ConstTableRef; +}; + + +inline ConstTableRef::ConstTableRef(const TableRef& other) noexcept + : m_table(other.m_table) + , m_instance_version(other.m_instance_version) +{ +} + +inline TableRef ConstTableRef::cast_away_const() const +{ + return TableRef(m_table, m_instance_version); +} + +inline std::ostream& operator<<(std::ostream& o, const ConstTableRef& tr) +{ + return tr.print(o); +} + +} // namespace realm + +#endif // REALM_TABLE_REF_HPP diff --git a/src/vendor-include/realm-ios/include/realm/table_view.hpp b/src/vendor-include/realm-ios/include/realm/table_view.hpp new file mode 100644 index 000000000..17a0a70fc --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/table_view.hpp @@ -0,0 +1,717 @@ +/************************************************************************* + * + * 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 + +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. + + +/// 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 ObjList { +public: + + /// Construct null view (no memory allocated). + ConstTableView() + : ObjList(&m_table_view_key_values) + , m_table_view_key_values(Allocator::get_default()) + { + } + + + /// Construct empty view, ready for addition of row indices. + ConstTableView(ConstTableRef parent); + ConstTableView(ConstTableRef parent, Query& query, size_t start, size_t end, size_t limit); + ConstTableView(ConstTableRef parent, ColKey column, const ConstObj& obj); + ConstTableView(ConstTableRef parent, ConstLnkLstPtr link_list); + + enum DistinctViewTag { DistinctView }; + ConstTableView(DistinctViewTag, ConstTableRef parent, ColKey column_key); + + /// Copy constructor. + ConstTableView(const ConstTableView&); + + /// Move constructor. + ConstTableView(ConstTableView&&) noexcept; + + ConstTableView& operator=(const ConstTableView&); + ConstTableView& operator=(ConstTableView&&) noexcept; + + ConstTableView(const ConstTableView& source, Transaction* tr, PayloadPolicy mode); + ConstTableView(ConstTableView& source, Transaction* tr, PayloadPolicy mode); + + ~ConstTableView() + { + m_key_values->destroy(); // Shallow + } + // - 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 + { + return m_key_values->size() == 0; + } + + // Tells if the table that this TableView points at still exists or has been deleted. + bool is_attached() const noexcept + { + return bool(m_table); + } + + bool is_obj_valid(size_t row_ndx) const noexcept + { + return m_table->is_valid(ObjKey(m_key_values->get(row_ndx))); + } + + // 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 + { + return m_query; + } + + std::unique_ptr clone() const + { + return std::unique_ptr(new ConstTableView(*this)); + } + + // 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. + std::unique_ptr clone_for_handover(Transaction* tr, PayloadPolicy mode) const + { + std::unique_ptr retval(new ConstTableView(*this, tr, mode)); + return retval; + } + template + R aggregate(ColKey column_key, size_t* result_count = nullptr, ObjKey* return_key = nullptr) const; + template + size_t aggregate_count(ColKey column_key, T count_target) const; + + int64_t sum_int(ColKey column_key) const; + int64_t maximum_int(ColKey column_key, ObjKey* return_key = nullptr) const; + int64_t minimum_int(ColKey column_key, ObjKey* return_key = nullptr) const; + double average_int(ColKey column_key, size_t* value_count = nullptr) const; + size_t count_int(ColKey column_key, int64_t target) const; + + double sum_float(ColKey column_key) const; + float maximum_float(ColKey column_key, ObjKey* return_key = nullptr) const; + float minimum_float(ColKey column_key, ObjKey* return_key = nullptr) const; + double average_float(ColKey column_key, size_t* value_count = nullptr) const; + size_t count_float(ColKey column_key, float target) const; + + double sum_double(ColKey column_key) const; + double maximum_double(ColKey column_key, ObjKey* return_key = nullptr) const; + double minimum_double(ColKey column_key, ObjKey* return_key = nullptr) const; + double average_double(ColKey column_key, size_t* value_count = nullptr) const; + size_t count_double(ColKey column_key, double target) const; + + Timestamp minimum_timestamp(ColKey column_key, ObjKey* return_key = nullptr) const; + Timestamp maximum_timestamp(ColKey column_key, ObjKey* return_key = nullptr) const; + size_t count_timestamp(ColKey column_key, Timestamp target) const; + + /// Search this view for the specified key. If found, the index of that row + /// within this view is returned, otherwise `realm::not_found` is returned. + size_t find_by_source_ndx(ObjKey key) const noexcept + { + return m_key_values->find_first(key); + } + + // Conversion + void to_json(std::ostream&, size_t link_depth = 0, std::map* renames = nullptr) 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 override; + + // A TableView is frozen if it is a) obtained from a query against a frozen table + // and b) is synchronized (is_in_sync()) + bool is_frozen() { return m_table->is_frozen() && is_in_sync(); } + // 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. + void sync_if_needed() const override; + // Return the version of the source it was created from. + TableVersions get_dependency_versions() const + { + TableVersions ret; + get_dependencies(ret); + return ret; + } + + + // Sort m_key_values according to one column + void sort(ColKey column, bool ascending = true); + + // Sort m_key_values 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 compound on the previous calls + void distinct(ColKey column); + void distinct(DistinctDescriptor columns); + void limit(LimitDescriptor limit); + void include(IncludeDescriptor include_paths); + IncludeDescriptor get_include_descriptors(); + + // Replace the order of sort and distinct operations, bypassing manually + // calling sort and distinct. This is a convenience method for bindings. + void apply_descriptor_ordering(const DescriptorOrdering& new_ordering); + + // Gets a readable and parsable string which completely describes the sort and + // distinct operations applied to this view. + std::string get_descriptor_ordering_description() const; + + // 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; + + bool is_backlink_view() const + { + return m_source_column_key != ColKey(); + } + +protected: + // This TableView can be "born" from 4 different sources: + // - LinkView + // - Query::find_all() + // - Table::get_distinct_view() + // - Table::get_backlink_view() + + void get_dependencies(TableVersions&) const override; + + void do_sync(); + + // The source column index that this view contain backlinks for. + ColKey m_source_column_key; + // The target object that rows in this view link to. + ObjKey m_linked_obj_key; + ConstTableRef m_linked_table; + + // If this TableView was created from a LinkList, then this reference points to it. Otherwise it's 0 + mutable ConstLnkLstPtr m_linklist_source; + + // m_distinct_column_source != ColKey() if this view was created from distinct values in a column of m_table. + ColKey m_distinct_column_source; + + // Stores the ordering criteria of applied sort and distinct operations. + DescriptorOrdering m_descriptor_ordering; + + // 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 = 0; + size_t m_end = size_t(-1); + size_t m_limit = size_t(-1); + + mutable TableVersions m_last_seen_versions; + +private: + KeyColumn m_table_view_key_values; // We should generally not use this name + ObjKey find_first_integer(ColKey column_key, int64_t value) const; + template + Timestamp minmax_timestamp(ColKey column_key, ObjKey* return_key) const; + RaceDetector m_race_detector; + + friend class Table; + friend class ConstObj; + friend class Query; + friend class DB; +}; + +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 ConstTableView { +public: + using ConstTableView::ConstTableView; + + TableView() = default; + + TableRef get_parent() noexcept + { + return m_table.cast_away_const(); + } + + // Rows + Obj get(size_t row_ndx); + Obj front(); + Obj back(); + Obj operator[](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. + /// + /// \param row_ndx The index within this table view of the row to be removed. + void remove(size_t row_ndx); + void remove_last(); + void clear(); + //@} + + std::unique_ptr clone() const + { + return std::unique_ptr(new TableView(*this)); + } + + std::unique_ptr clone_for_handover(Transaction* tr, PayloadPolicy policy) const + { + std::unique_ptr retval(new TableView(*this, tr, policy)); + return retval; + } + +private: + TableView(TableRef parent); + TableView(TableRef parent, Query& query, size_t start, size_t end, size_t limit); + TableView(TableRef parent, ConstLnkLstPtr); + TableView(DistinctViewTag, TableRef parent, ColKey column_key); + + friend class ConstTableView; + friend class Table; + friend class Query; + friend class LnkLst; +}; + + + + +// ================================================================================================ +// ConstTableView Implementation: + +inline ConstTableView::ConstTableView(ConstTableRef parent) + : ObjList(&m_table_view_key_values, parent) // Throws + , m_table_view_key_values(Allocator::get_default()) +{ + m_table_view_key_values.create(); + if (m_table) { + m_last_seen_versions.emplace_back(m_table->get_key(), m_table->get_content_version()); + } +} + +inline ConstTableView::ConstTableView(ConstTableRef parent, Query& query, size_t start, size_t end, size_t lim) + : ObjList(&m_table_view_key_values, parent) + , m_query(query) + , m_start(start) + , m_end(end) + , m_limit(lim) + , m_table_view_key_values(Allocator::get_default()) +{ + m_table_view_key_values.create(); +} + +inline ConstTableView::ConstTableView(ConstTableRef src_table, ColKey src_column_key, const ConstObj& obj) + : ObjList(&m_table_view_key_values, src_table) // Throws + , m_source_column_key(src_column_key) + , m_linked_obj_key(obj.get_key()) + , m_linked_table(obj.get_table()) + , m_table_view_key_values(Allocator::get_default()) +{ + m_table_view_key_values.create(); + if (m_table) { + m_last_seen_versions.emplace_back(m_table->get_key(), m_table->get_content_version()); + m_last_seen_versions.emplace_back(obj.get_table()->get_key(), obj.get_table()->get_content_version()); + } +} + +inline ConstTableView::ConstTableView(DistinctViewTag, ConstTableRef parent, ColKey column_key) + : ObjList(&m_table_view_key_values, parent) // Throws + , m_distinct_column_source(column_key) + , m_table_view_key_values(Allocator::get_default()) +{ + REALM_ASSERT(m_distinct_column_source != ColKey()); + m_table_view_key_values.create(); + if (m_table) { + m_last_seen_versions.emplace_back(m_table->get_key(), m_table->get_content_version()); + } +} + +inline ConstTableView::ConstTableView(ConstTableRef parent, ConstLnkLstPtr link_list) + : ObjList(&m_table_view_key_values, parent) // Throws + , m_linklist_source(std::move(link_list)) + , m_table_view_key_values(Allocator::get_default()) +{ + REALM_ASSERT(m_linklist_source); + m_table_view_key_values.create(); + if (m_table) { + m_last_seen_versions.emplace_back(m_table->get_key(), m_table->get_content_version()); + } +} + +inline ConstTableView::ConstTableView(const ConstTableView& tv) + : ObjList(&m_table_view_key_values, tv.m_table) + , m_source_column_key(tv.m_source_column_key) + , m_linked_obj_key(tv.m_linked_obj_key) + , m_linked_table(tv.m_linked_table) + , m_linklist_source(tv.m_linklist_source ? tv.m_linklist_source->clone() : LnkLstPtr{}) + , m_distinct_column_source(tv.m_distinct_column_source) + , m_descriptor_ordering(tv.m_descriptor_ordering) + , m_query(tv.m_query) + , m_start(tv.m_start) + , m_end(tv.m_end) + , m_limit(tv.m_limit) + , m_last_seen_versions(tv.m_last_seen_versions) + , m_table_view_key_values(tv.m_table_view_key_values) +{ + m_limit_count = tv.m_limit_count; +} + +inline ConstTableView::ConstTableView(ConstTableView&& tv) noexcept + : ObjList(&m_table_view_key_values, tv.m_table) + , m_source_column_key(tv.m_source_column_key) + , m_linked_obj_key(tv.m_linked_obj_key) + , m_linked_table(tv.m_linked_table) + , m_linklist_source(std::move(tv.m_linklist_source)) + , m_distinct_column_source(tv.m_distinct_column_source) + , m_descriptor_ordering(std::move(tv.m_descriptor_ordering)) + , 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_versions(std::move(tv.m_last_seen_versions)) + , m_table_view_key_values(std::move(tv.m_table_view_key_values)) +{ + m_limit_count = tv.m_limit_count; +} + +inline ConstTableView& ConstTableView::operator=(ConstTableView&& tv) noexcept +{ + m_table = std::move(tv.m_table); + + m_table_view_key_values = std::move(tv.m_table_view_key_values); + m_query = std::move(tv.m_query); + m_last_seen_versions = tv.m_last_seen_versions; + m_start = tv.m_start; + m_end = tv.m_end; + m_limit = tv.m_limit; + m_limit_count = tv.m_limit_count; + m_source_column_key = tv.m_source_column_key; + m_linked_obj_key = tv.m_linked_obj_key; + m_linked_table = tv.m_linked_table; + m_linklist_source = std::move(tv.m_linklist_source); + m_descriptor_ordering = std::move(tv.m_descriptor_ordering); + m_distinct_column_source = tv.m_distinct_column_source; + + return *this; +} + +inline ConstTableView& ConstTableView::operator=(const ConstTableView& tv) +{ + if (this == &tv) + return *this; + + m_table_view_key_values = tv.m_table_view_key_values; + + m_query = tv.m_query; + m_last_seen_versions = tv.m_last_seen_versions; + m_start = tv.m_start; + m_end = tv.m_end; + m_limit = tv.m_limit; + m_limit_count = tv.m_limit_count; + m_source_column_key = tv.m_source_column_key; + m_linked_obj_key = tv.m_linked_obj_key; + m_linked_table = tv.m_linked_table; + m_linklist_source = tv.m_linklist_source ? tv.m_linklist_source->clone() : LnkLstPtr{}; + m_descriptor_ordering = tv.m_descriptor_ordering; + m_distinct_column_source = tv.m_distinct_column_source; + + return *this; +} + +#define REALM_ASSERT_COLUMN(column_key) \ + m_table.check(); \ + REALM_ASSERT(m_table->colkey2ndx(column_key)) + +#define REALM_ASSERT_ROW(row_ndx) \ + m_table.check(); \ + REALM_ASSERT(row_ndx < m_key_values->size()) + +#define REALM_ASSERT_COLUMN_AND_TYPE(column_key, column_type) \ + REALM_ASSERT_COLUMN(column_key); \ + REALM_DIAG_PUSH(); \ + REALM_DIAG_IGNORE_TAUTOLOGICAL_COMPARE(); \ + REALM_ASSERT(m_table->get_column_type(column_key) == column_type); \ + REALM_DIAG_POP() + +#define REALM_ASSERT_INDEX(column_key, row_ndx) \ + REALM_ASSERT_COLUMN(column_key); \ + REALM_ASSERT(row_ndx < m_key_values->size()) + +#define REALM_ASSERT_INDEX_AND_TYPE(column_key, row_ndx, column_type) \ + REALM_ASSERT_COLUMN_AND_TYPE(column_key, column_type); \ + REALM_ASSERT(row_ndx < m_key_values->size()) + +#define REALM_ASSERT_INDEX_AND_TYPE_TABLE_OR_MIXED(column_key, row_ndx) \ + REALM_ASSERT_COLUMN(column_key); \ + REALM_DIAG_PUSH(); \ + REALM_DIAG_IGNORE_TAUTOLOGICAL_COMPARE(); \ + REALM_ASSERT(m_table->get_column_type(column_key) == type_Table || \ + (m_table->get_column_type(column_key) == type_Mixed)); \ + REALM_DIAG_POP(); \ + REALM_ASSERT(row_ndx < m_key_values->size()) + +template +ConstTableView ObjList::find_all(ColKey column_key, T value) +{ + ConstTableView tv(m_table); + auto keys = tv.m_key_values; + for_each([column_key, value, &keys](ConstObj& o) { + if (o.get(column_key) == value) { + keys->add(o.get_key()); + } + return false; + }); + return tv; +} + +//-------------------------- TableView, ConstTableView implementation: + + +inline void TableView::remove_last() +{ + if (!is_empty()) + remove(size() - 1); +} + +inline TableView::TableView(TableRef parent) + : ConstTableView(parent) +{ +} + +inline TableView::TableView(TableRef parent, Query& query, size_t start, size_t end, size_t lim) + : ConstTableView(parent, query, start, end, lim) +{ +} + +inline TableView::TableView(TableRef parent, ConstLnkLstPtr link_list) + : ConstTableView(parent, std::move(link_list)) +{ +} + +inline TableView::TableView(ConstTableView::DistinctViewTag, TableRef parent, ColKey column_key) + : ConstTableView(ConstTableView::DistinctView, parent, column_key) +{ +} + +// Rows +inline Obj TableView::get(size_t row_ndx) +{ + REALM_ASSERT_ROW(row_ndx); + ObjKey key(m_key_values->get(row_ndx)); + REALM_ASSERT(key != realm::null_key); + return get_parent()->get_object(key); +} + +inline Obj TableView::front() +{ + return get(0); +} + +inline Obj TableView::back() +{ + size_t last_row_ndx = size() - 1; + return get(last_row_ndx); +} + +inline Obj TableView::operator[](size_t row_ndx) +{ + return get(row_ndx); +} + +} // namespace realm + +#endif // REALM_TABLE_VIEW_HPP diff --git a/src/vendor-include/realm-ios/include/realm/timestamp.hpp b/src/vendor-include/realm-ios/include/realm/timestamp.hpp new file mode 100644 index 000000000..20265f1eb --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/timestamp.hpp @@ -0,0 +1,233 @@ +/************************************************************************* + * + * 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 +#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) + { + } + template + Timestamp(std::chrono::time_point tp) + : m_is_null(false) + { + int64_t native_nano = std::chrono::duration_cast(tp.time_since_epoch()).count(); + m_seconds = native_nano / nanoseconds_per_second; + m_nanoseconds = static_cast(native_nano % nanoseconds_per_second); + } + 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; + } + + template + std::chrono::time_point get_time_point() + { + REALM_ASSERT(!m_is_null); + + int64_t native_nano = m_seconds * nanoseconds_per_second + m_nanoseconds; + auto duration = std::chrono::duration_cast(std::chrono::duration{native_nano}); + + return std::chrono::time_point(duration); + } + + 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 + { + if (is_null()) { + return false; + } + if (rhs.is_null()) { + return true; + } + return (m_seconds > rhs.m_seconds) || (m_seconds == rhs.m_seconds && m_nanoseconds > rhs.m_nanoseconds); + } + bool operator<(const Timestamp& rhs) const + { + if (rhs.is_null()) { + return false; + } + if (is_null()) { + return true; + } + return (m_seconds < rhs.m_seconds) || (m_seconds == rhs.m_seconds && m_nanoseconds < rhs.m_nanoseconds); + } + bool operator<=(const Timestamp& rhs) const + { + if (is_null()) { + return true; + } + if (rhs.is_null()) { + return false; + } + return *this < rhs || *this == rhs; + } + bool operator>=(const Timestamp& rhs) const + { + if (rhs.is_null()) { + return true; + } + if (is_null()) { + return false; + } + 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) +{ + auto seconds = time_t(d.get_seconds()); + struct tm buf; +#ifdef _MSC_VER + bool success = gmtime_s(&buf, &seconds) == 0; +#else + bool success = gmtime_r(&seconds, &buf) != nullptr; +#endif + if (success) { + // We need a buffer for formatting dates. + // Max size is 20 bytes (incl terminating zero) "YYYY-MM-DD HH:MM:SS"\0 + char buffer[30]; + if (strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", &buf)) { + out << buffer; + } + } + + return out; +} +// LCOV_EXCL_STOP + +} // namespace realm + +namespace std { +template <> +struct numeric_limits { + static constexpr bool is_integer = false; + static realm::Timestamp min() + { + return realm::Timestamp(numeric_limits::min(), 0); + } + static realm::Timestamp lowest() + { + return realm::Timestamp(numeric_limits::lowest(), 0); + } + static realm::Timestamp max() + { + return realm::Timestamp(numeric_limits::max(), 0); + } +}; +} + +#endif // REALM_TIMESTAMP_HPP diff --git a/src/vendor-include/realm-ios/include/realm/unicode.hpp b/src/vendor-include/realm-ios/include/realm/unicode.hpp new file mode 100644 index 000000000..d8b3d3390 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/unicode.hpp @@ -0,0 +1,163 @@ +/************************************************************************* + * + * 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); + +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/src/vendor-include/realm-ios/include/realm/util/aes_cryptor.hpp b/src/vendor-include/realm-ios/include/realm/util/aes_cryptor.hpp new file mode 100644 index 000000000..23720b180 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/aes_cryptor.hpp @@ -0,0 +1,113 @@ +/************************************************************************* + * + * 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 +#include +#include +#include +#include + +#if REALM_ENABLE_ENCRYPTION + +#if REALM_PLATFORM_APPLE +#include +#elif defined(_WIN32) +#include +#include +#include +#pragma comment(lib, "bcrypt.lib") +#else +#include +#include +#endif + +namespace realm { +namespace util { + +struct iv_table; +class EncryptedFileMapping; + +class AESCryptor { +public: + AESCryptor(const uint8_t* key); + ~AESCryptor() noexcept; + + void set_file_size(off_t new_size); + + bool read(FileDesc fd, off_t pos, char* dst, size_t size); + void write(FileDesc fd, off_t pos, const char* src, size_t size) noexcept; + +private: + enum EncryptionMode { +#if REALM_PLATFORM_APPLE + mode_Encrypt = kCCEncrypt, + mode_Decrypt = kCCDecrypt +#elif defined(_WIN32) + mode_Encrypt = 0, + mode_Decrypt = 1 +#else + mode_Encrypt = 1, + mode_Decrypt = 0 +#endif + }; + +#if REALM_PLATFORM_APPLE + CCCryptorRef m_encr; + CCCryptorRef m_decr; +#elif defined(_WIN32) + BCRYPT_KEY_HANDLE m_aes_key_handle; +#else + uint8_t m_aesKey[32]; + EVP_CIPHER_CTX* m_ctx; +#endif + + uint8_t m_hmacKey[32]; + std::vector m_iv_buffer; + std::unique_ptr m_rw_buffer; + std::unique_ptr m_dst_buffer; + + void calc_hmac(const void* src, size_t len, uint8_t* dst, const uint8_t* key) const; + bool check_hmac(const void* data, size_t len, const uint8_t* hmac) const; + void crypt(EncryptionMode mode, off_t pos, char* dst, const char* src, const char* stored_iv) noexcept; + iv_table& get_iv_table(FileDesc fd, off_t data_pos) noexcept; + void handle_error(); +}; + +struct ReaderInfo { + const void* reader_ID; + uint64_t version; +}; + +struct SharedFileInfo { + FileDesc fd; + AESCryptor cryptor; + std::vector mappings; + uint64_t last_scanned_version = 0; + uint64_t current_version = 0; + size_t num_decrypted_pages = 0; + size_t num_reclaimed_pages = 0; + size_t progress_index = 0; + std::vector readers; + + SharedFileInfo(const uint8_t* key, FileDesc file_descriptor); +}; +} +} + +#endif // REALM_ENABLE_ENCRYPTION diff --git a/src/vendor-include/realm-ios/include/realm/util/allocation_metrics.hpp b/src/vendor-include/realm-ios/include/realm/util/allocation_metrics.hpp new file mode 100644 index 000000000..10176e757 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/allocation_metrics.hpp @@ -0,0 +1,326 @@ +/************************************************************************* + * + * REALM CONFIDENTIAL + * __________________ + * + * [2011] - [2018] 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_ALLOCATION_METRICS_HPP +#define REALM_UTIL_ALLOCATION_METRICS_HPP + +#include +#include + +#include + +namespace realm { +namespace util { + +/// Designate a name to be used in heap allocation metrics. +/// +/// An instance can be used with `AllocationMetricsContext::get_metric()` to +/// obtain an instance of `MeteredAllocator` that counts +/// allocations/deallocations towards this name, within that context. +/// +/// Instances of `AllocationMetricName` should be statically allocated. When an +/// instance has been initialized, it must not be destroyed until the program +/// terminates. This is to ensure that iterating over existing names is +/// thread-safe and lock-free. +/// +/// Similarly, when an instance of `AllocationMetricsContext` has been +/// allocated, no further instances of AllocationMetricName must be +/// instantiated. +struct AllocationMetricName { + explicit AllocationMetricName(const char* name) noexcept; + + /// Get the string name. + /// + /// This method is thread-safe. + const char* name() const noexcept; + + /// Get the index of this metric. The index corresponds to an allocator + /// inside the current instance of AllocatorMetricTenant. + /// + /// This method is thread-safe. + size_t index() const noexcept; + + /// Get the next name. The names are returned in no particular order. + /// + /// This method is thread-safe. + const AllocationMetricName* next() const noexcept; + + /// Get the first name in the internal list of names, for the purpose + /// of iterating over all names in the program. + /// + /// This method is thread-safe. + static const AllocationMetricName* get_top() noexcept; + static const AllocationMetricName* find(const char* name) noexcept; +private: + const char* m_name; + size_t m_index; // Index into `AllocationMetricsContext::m_metrics`. + + // This is used to iterate over all existing components. Instances of + // AllocationMetricName are expected to be statically allocated. + const AllocationMetricName* m_next = nullptr; +}; + + +/// A heap memory allocator that keeps track of how much was +/// allocated/deallocated throughout its lifetime. +/// +/// Memory is allocated with `DefaultAllocator`. +/// +/// All methods on instances of this class are thread-safe. +class MeteredAllocator final : public AllocatorBase { +public: + MeteredAllocator() noexcept; + + static MeteredAllocator& unknown() noexcept; + + /// Return the currently allocated number of bytes. + /// + /// This method is thread-safe, but may temporarily return slightly + /// inaccurate results if allocations/deallocations are happening while it + /// is being called. + std::size_t get_currently_allocated_bytes() const noexcept; + + /// Return the total number of bytes that have been allocated (including + /// allocations that have since been freed). + /// + /// This method is thread-safe. + std::size_t get_total_allocated_bytes() const noexcept; + + /// Return the total number of bytes that have been freed. + /// + /// This method is thread-safe. + std::size_t get_total_deallocated_bytes() const noexcept; + + // AllocatorBase interface: + + /// Return a reference to an MeteredAllocator that belongs to the current + /// AllocationMetricsContext (if any) and the current AllocationMetricNameScope + /// (if any). + /// + /// The returned reference is valid for the duration of the lifetime of the + /// instance of AllocationMetricsContext that is "current" at the time of + /// calling this function, and namely it is valid beyond the lifetime of + /// the current AllocationMetricNameScope. + /// + /// If there is no current AllocationMetricsContext, the global "unknown" + /// tenant will be used. + /// + /// If no metric name is currently in scope (through the use of + /// AllocationMetricNameScope), allocations and deallocations will be counted + /// towards the default "unknown" metric. + /// + /// This method is thread-safe. + static MeteredAllocator& get_default() noexcept; + + /// Allocate memory, accounting for the allocation in metrics. + /// + /// This method is thread-safe. + void* allocate(size_t size, size_t align) override final; + + /// Free memory, accounting for the deallocation in metrics. + /// + /// This method is thread-safe. + void free(void* ptr, size_t size) noexcept override final; + + /// Notify metrics that an allocation happened. + /// + /// This method is thread-safe. + void did_allocate_bytes(std::size_t) noexcept; + + /// Notify metrics that a deallocation happened. + /// + /// This method is thread-safe. + void did_free_bytes(std::size_t) noexcept; + +private: + std::atomic m_allocated_bytes; + // These members are spaced by 64 bytes to prevent false sharing + // (inter-processor CPU locks when multiple processes are modifying them + // concurrently). + char dummy[56]; + std::atomic m_deallocated_bytes; + char dummy2[56]; // Prevent false sharing with the next element. +}; + +/// `AllocationMetricsContext` represents a runtime scope for metrics, such as +/// for instance a server running in a multi-tenant scenario, where each tenant +/// would have one context associated with it. +/// +/// `AllocationMetricsContext` is not available on mobile, due to lack of +/// thread-local storage support on iOS. +struct AllocationMetricsContext { +public: + AllocationMetricsContext(); + ~AllocationMetricsContext(); + +#if !REALM_MOBILE + /// Get the thread-specific AllocationMetricsContext. If none has been set, a + /// reference to a globally-allocated "unknown" tenant will be returned. + static AllocationMetricsContext& get_current() noexcept; +#endif + + /// Get the statically-allocated "unknown" tenant. + static AllocationMetricsContext& get_unknown(); + + MeteredAllocator& get_metric(const AllocationMetricName& name) noexcept; +private: + std::unique_ptr m_metrics; + + // In debug builds, this is incremented/decremented by + // `AllocationMetricsContextScope`, and checked in the destructor, to avoid + // dangling references. + std::atomic m_refcount; + friend class AllocationMetricsContextScope; +}; + +/// Open a scope where metered memory allocations are counted towards the given +/// name. +/// +/// Creating an instance of this class causes calls to +/// `MeteredAllocator::get_default()` from the current thread to return a +/// reference to an allocator that accounts for allocations/deallocations +/// under the named metric specified as the constructor argument. +/// +/// When such an instance is destroyed, the previous scope will come back +/// in effect (if one exists; if none exists, the "unknown" metric will be +/// used). +/// +/// It is usually an error to create instances of this class with non-scope +/// lifetime, for example on the heap. For that reason, `operator new` is +/// disabled as a precaution. +/// +/// If no `AllocationMetricsContext` is current (by instantiation of +/// `AllocationMetricsContextScope`), metrics recorded in the scope introduced +/// by this instance will count towards the "unknown" context, accessible by +/// calling `AllocationMetricsContext::get_unknown()`. +class AllocationMetricNameScope final { +public: + /// Establish a scope under which all allocations will be tracked as + /// belonging to \a name. + explicit AllocationMetricNameScope(const AllocationMetricName& name) noexcept; + ~AllocationMetricNameScope(); + AllocationMetricNameScope(AllocationMetricNameScope&&) = delete; + AllocationMetricNameScope& operator=(AllocationMetricNameScope&&) = delete; + + void* operator new(std::size_t) = delete; +private: + const AllocationMetricName& m_name; + const AllocationMetricName* m_previous = nullptr; +}; + +/// Open a scope using the given context for allocation metrics. +/// +/// Creating an instance of this class causes calls to +/// `AllocationMetricsContext::get_current()` to return the provided +/// instance. This function is called when by `MeteredAllocator::get_default()` +/// to return an instance that belongs to the given context. +/// +/// When the instance is destroyed, the previous context will become active, or +/// the "unknown" context if there was none. +/// +/// It is usually an error to create instances of this class with non-scope +/// lifetime, for example on the heap. For that reason, `operator new` is +/// disabled as a precaution. +class AllocationMetricsContextScope final { +public: + explicit AllocationMetricsContextScope(AllocationMetricsContext& context) noexcept; + ~AllocationMetricsContextScope(); + AllocationMetricsContextScope(AllocationMetricsContextScope&&) = delete; + AllocationMetricsContextScope& operator=(AllocationMetricsContextScope&&) = delete; + + void* operator new(std::size_t) = delete; + +private: + AllocationMetricsContext& m_context; + AllocationMetricsContext& m_previous; +}; + + +/// Convenience STL-compatible allocator that counts allocations as part of the +/// current AllocationMetricNameScope. +template +using MeteredSTLAllocator = STLAllocator; + + +// Implementation: + +inline const char* AllocationMetricName::name() const noexcept +{ + return m_name; +} + +inline size_t AllocationMetricName::index() const noexcept +{ + return m_index; +} + +inline const AllocationMetricName* AllocationMetricName::next() const noexcept +{ + return m_next; +} + +inline std::size_t MeteredAllocator::get_currently_allocated_bytes() const noexcept +{ + return get_total_allocated_bytes() - get_total_deallocated_bytes(); +} + +inline std::size_t MeteredAllocator::get_total_allocated_bytes() const noexcept +{ + return m_allocated_bytes.load(std::memory_order_relaxed); +} + +inline std::size_t MeteredAllocator::get_total_deallocated_bytes() const noexcept +{ + return m_deallocated_bytes.load(std::memory_order_relaxed); +} + +inline void* MeteredAllocator::allocate(size_t size, size_t align) +{ + void* ptr = DefaultAllocator::get_default().allocate(size, align); + did_allocate_bytes(size); + return ptr; +} + +inline void MeteredAllocator::free(void* ptr, size_t size) noexcept +{ + DefaultAllocator::get_default().free(ptr, size); + did_free_bytes(size); +} + +inline void MeteredAllocator::did_allocate_bytes(std::size_t size) noexcept +{ +#if !REALM_MOBILE + m_allocated_bytes.fetch_add(size, std::memory_order_relaxed); +#else + static_cast(size); +#endif +} + +inline void MeteredAllocator::did_free_bytes(std::size_t size) noexcept +{ +#if !REALM_MOBILE + m_deallocated_bytes.fetch_add(size, std::memory_order_relaxed); +#else + static_cast(size); +#endif +} +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_ALLOCATION_METRICS_HPP diff --git a/src/vendor-include/realm-ios/include/realm/util/allocator.hpp b/src/vendor-include/realm-ios/include/realm/util/allocator.hpp new file mode 100644 index 000000000..6db41a650 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/allocator.hpp @@ -0,0 +1,389 @@ +/************************************************************************* + * + * Copyright 2018 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_ALLOCATOR_HPP +#define REALM_UTIL_ALLOCATOR_HPP + +#include +#include +#include + +namespace realm { +namespace util { + +/// Dynamic heap allocation interface. +/// +/// Implementors may optionally implement a static method `get_default()`, which +/// should return a reference to an allocator instance. This allows +/// `STLAllocator` to be default-constructed. +/// +/// NOTE: This base class is not related to the `realm::Allocator` interface, +/// which is used in the context of allocating memory inside a Realm file. +struct AllocatorBase { + static constexpr std::size_t max_alignment = 16; // FIXME: This is arch-dependent + + /// Allocate \a size bytes at aligned at \a align. + /// + /// May throw `std::bad_alloc` if allocation fails. May **NOT** return + /// an invalid pointer (such as `nullptr`). + virtual void* allocate(std::size_t size, std::size_t align) = 0; + + /// Free the previously allocated block of memory. \a size is not required + /// to be accurate, and is only provided for statistics and debugging + /// purposes. + /// + /// \a ptr may be `nullptr`, in which case this shall be a noop. + virtual void free(void* ptr, size_t size) noexcept = 0; +}; + +/// Implementation of AllocatorBase that uses `operator new`/`operator delete`. +/// +/// Using this allocator with standard containers is zero-overhead: No +/// additional storage is required at any level. +struct DefaultAllocator final : AllocatorBase { + /// Return a reference to a global singleton. + /// + /// This method is thread-safe. + static DefaultAllocator& get_default() noexcept; + + /// Allocate memory (using `operator new`). + /// + /// \a align must not exceed `max_alignment` before C++17. + /// + /// This method is thread-safe. + void* allocate(std::size_t size, std::size_t align) final; + + /// Free memory (using `operator delete`). + /// + /// If \a ptr equals `nullptr`, this is a no-op. + /// + /// This method is thread-safe. + void free(void* ptr, std::size_t size) noexcept final; + +private: + static DefaultAllocator g_instance; + DefaultAllocator() {} +}; + +template +struct STLDeleter; + +namespace detail { +/// Base class for things that hold a reference to an allocator. The default +/// implementation carries a pointer to the allocator instance. Singleton +/// allocators (such as `DefaultAllocator`) may specialize this class such that +/// no extra storage is needed. +template +struct GetAllocator { + // Note: Some allocators may not define get_default(). This is OK, and + // this constructor will not be instantiated (SFINAE). + GetAllocator() noexcept + : m_allocator(&Allocator::get_default()) + { + } + + template + GetAllocator(A& allocator) noexcept + : m_allocator(&allocator) + { + } + + template + GetAllocator& operator=(const GetAllocator& other) noexcept + { + m_allocator = &other.get_allocator(); + return *this; + } + + Allocator& get_allocator() const noexcept + { + return *m_allocator; + } + + bool operator==(const GetAllocator& other) const noexcept + { + return m_allocator == other.m_allocator; + } + + bool operator!=(const GetAllocator& other) const noexcept + { + return m_allocator != other.m_allocator; + } + + Allocator* m_allocator; +}; + +/// Specialization for `DefaultAllocator` that has zero size, i.e. no extra +/// storage requirements compared with `std::allocator`. +template <> +struct GetAllocator { + GetAllocator() noexcept {} + + GetAllocator(DefaultAllocator&) noexcept {} + + DefaultAllocator& get_allocator() const noexcept + { + return DefaultAllocator::get_default(); + } + + bool operator==(const GetAllocator&) const noexcept + { + return true; + } + + bool operator!=(const GetAllocator&) const noexcept + { + return false; + } +}; +} // namespace detail + +/// STL-compatible static dispatch bridge to a dynamic implementation of +/// `AllocatorBase`. Wraps a pointer to an object that adheres to the +/// `AllocatorBase` interface. It is optional whether the `Allocator` class +/// template argument actually derives from `AllocatorBase`. +/// +/// The intention is that users of this class can set `Allocator` to the +/// nearest-known base class of the expected allocator implementations, such +/// that appropriate devirtualization can take place. +template +struct STLAllocator : detail::GetAllocator { + using value_type = T; + using Deleter = STLDeleter; + + // These typedefs are optional, but GCC 4.9 requires them when using the + // allocator together with std::map, std::basic_string, etc. + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using reference = T&; + using const_reference = const T&; + using pointer = T*; + using const_pointer = const T*; + + /// The default constructor is only availble when the static method + /// `Allocator::get_default()` exists. + STLAllocator() noexcept {} + + constexpr STLAllocator(Allocator& base) noexcept + : detail::GetAllocator(base) + { + } + template + constexpr STLAllocator(const STLAllocator& other) noexcept + : detail::GetAllocator(other.get_allocator()) + { + } + + STLAllocator& operator=(const STLAllocator& other) noexcept = default; + + T* allocate(std::size_t n) + { + static_assert(alignof(T) <= Allocator::max_alignment, "Over-aligned allocation"); + void* ptr = this->get_allocator().allocate(sizeof(T) * n, alignof(T)); + return static_cast(ptr); + } + + void deallocate(T* ptr, std::size_t n) noexcept + { + this->get_allocator().free(ptr, sizeof(T) * n); + } + + operator Allocator&() const + { + return this->get_allocator(); + } + + template + struct rebind { + using other = STLAllocator; + }; + + // construct() and destroy() are optional, but are required by some + // containers under GCC 4.9 (verified for at least std::list). + template + void construct(T* ptr, Args&&... args) + { + ::new (ptr) T(std::forward(args)...); + } + + template + void destroy(U* ptr) + { + ptr->~U(); + } + +private: + template + friend struct STLAllocator; +}; + +template +struct STLDeleter : detail::GetAllocator { + // The reason for this member is to accurately pass `size` to `free()` when + // deallocating. `sizeof(T)` may not be good enough, because the pointer may + // have been cast to a relative type of different size. + size_t m_size; + + explicit STLDeleter(Allocator& allocator) noexcept + : STLDeleter(0, allocator) + { + } + explicit STLDeleter(size_t size, Allocator& allocator) noexcept + : detail::GetAllocator(allocator) + , m_size(size) + { + } + + template + STLDeleter(const STLDeleter& other) noexcept + : detail::GetAllocator(other.get_allocator()) + , m_size(other.m_size) + { + } + + void operator()(T* ptr) + { + ptr->~T(); + this->get_allocator().free(ptr, m_size); + } +}; + +template +struct STLDeleter : detail::GetAllocator { + // Note: Array-allocated pointers cannot be upcast to base classes, because + // of array slicing. + size_t m_count; + explicit STLDeleter(Allocator& allocator) noexcept + : STLDeleter(0, allocator) + { + } + explicit STLDeleter(size_t count, Allocator& allocator) noexcept + : detail::GetAllocator(allocator) + , m_count(count) + { + } + + template + STLDeleter(const STLDeleter& other) noexcept + : detail::GetAllocator(other.get_allocator()) + , m_count(other.m_count) + { + } + + template + STLDeleter& operator=(const STLDeleter& other) noexcept + { + static_cast&>(*this) = static_cast&>(other); + m_count = other.m_count; + return *this; + } + + void operator()(T* ptr) + { + for (size_t i = 0; i < m_count; ++i) { + ptr[i].~T(); + } + this->get_allocator().free(ptr, m_count * sizeof(T)); + } +}; + +/// make_unique with custom allocator (non-array version) +template +auto make_unique(Allocator& allocator, Args&&... args) + -> std::enable_if_t::value, std::unique_ptr>> +{ + void* memory = allocator.allocate(sizeof(T), alignof(T)); // Throws + T* ptr; + try { + ptr = new (memory) T(std::forward(args)...); // Throws + } + catch (...) { + allocator.free(memory, sizeof(T)); + throw; + } + std::unique_ptr> result{ptr, STLDeleter{sizeof(T), allocator}}; + return result; +} + +/// make_unique with custom allocator supporting `get_default()` +/// (non-array-version) +template +auto make_unique(Args&&... args) + -> std::enable_if_t::value, std::unique_ptr>> +{ + return make_unique(Allocator::get_default(), std::forward(args)...); +} + +/// make_unique with custom allocator (array version) +template +auto make_unique(Allocator& allocator, size_t count) + -> std::enable_if_t::value, std::unique_ptr>> +{ + using T = std::remove_extent_t; + void* memory = allocator.allocate(sizeof(T) * count, alignof(T)); // Throws + T* ptr = reinterpret_cast(memory); + size_t constructed = 0; + try { + // FIXME: Can't use array placement new, because MSVC has a buggy + // implementation of it. :-( + while (constructed < count) { + new (&ptr[constructed]) T; // Throws + ++constructed; + } + } + catch (...) { + for (size_t i = 0; i < constructed; ++i) { + ptr[i].~T(); + } + allocator.free(memory, sizeof(T) * count); + throw; + } + std::unique_ptr> result{ptr, STLDeleter{count, allocator}}; + return result; +} + +/// make_unique with custom allocator supporting `get_default()` (array version) +template +auto make_unique(size_t count) + -> std::enable_if_t::value, std::unique_ptr>> +{ + return make_unique(Allocator::get_default(), count); +} + + +// Implementation: + +inline DefaultAllocator& DefaultAllocator::get_default() noexcept +{ + return g_instance; +} + +inline void* DefaultAllocator::allocate(std::size_t size, std::size_t) +{ + return new char[size]; +} + +inline void DefaultAllocator::free(void* ptr, std::size_t) noexcept +{ + delete[] static_cast(ptr); +} + +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_ALLOCATOR_HPP diff --git a/src/vendor-include/realm-ios/include/realm/util/any.hpp b/src/vendor-include/realm-ios/include/realm/util/any.hpp new file mode 100644 index 000000000..5ac72b236 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/any.hpp @@ -0,0 +1,165 @@ +//////////////////////////////////////////////////////////////////////////// +// +// 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_ANY_HPP +#define REALM_UTIL_ANY_HPP + +#include +#include +#include +#include + +#include + +namespace realm { +namespace util { + +using bad_cast = ExceptionWithBacktrace; + +// A naive implementation of C++17's std::any +// This does not perform the small-object optimization or make any particular +// attempt at being performant +class Any final { +public: + // Constructors + + Any() = default; + Any(Any&&) noexcept = default; + ~Any() = default; + Any& operator=(Any&&) noexcept = default; + + Any(Any const& rhs) + : m_value(rhs.m_value ? rhs.m_value->copy() : nullptr) + { + } + + template::type, Any>::value>::type> + Any(T&& value) + : m_value(std::make_unique::type>>(std::forward(value))) + { + } + + Any& operator=(Any const& rhs) + { + m_value = rhs.m_value ? rhs.m_value->copy() : nullptr; + return *this; + } + + template::type, Any>::value>::type> + Any& operator=(T&& value) + { + m_value = std::make_unique::type>>(std::forward(value)); + return *this; + } + + // Modifiers + + void reset() noexcept { m_value.reset(); } + void swap(Any& rhs) noexcept { std::swap(m_value, rhs.m_value); } + + // Observers + + bool has_value() const noexcept { return m_value != nullptr; } + std::type_info const& type() const noexcept { return m_value ? m_value->type() : typeid(void); } + +private: + struct ValueBase { + virtual ~ValueBase() noexcept { } + virtual std::type_info const& type() const noexcept = 0; + virtual std::unique_ptr copy() const = 0; + }; + template + struct Value : ValueBase { + T value; + template Value(U&& v) : value(std::forward(v)) { } + + std::type_info const& type() const noexcept override { return typeid(T); } + std::unique_ptr copy() const override + { + return std::make_unique>(value); + } + }; + std::unique_ptr m_value; + + template + friend const T* any_cast(const Any* operand) noexcept; + template + friend T* any_cast(Any* operand) noexcept; + + template + const T* cast() const noexcept + { + return &static_cast*>(m_value.get())->value; + } + + template + T* cast() noexcept + { + return &static_cast*>(m_value.get())->value; + } +}; + +template +T any_cast(Any const& value) +{ + auto ptr = any_cast::type>::type>(&value); + if (!ptr) + throw bad_cast(); + return *ptr; +} + +template +T any_cast(Any& value) +{ + auto ptr = any_cast::type>(&value); + if (!ptr) + throw bad_cast(); + return *ptr; +} + +template +T any_cast(Any&& value) +{ + auto ptr = any_cast::type>(&value); + if (!ptr) + throw bad_cast(); + return std::move(*ptr); +} + +template +T* any_cast(Any* value) noexcept +{ + return value && value->type() == typeid(T) ? value->cast() : nullptr; +} + +template +const T* any_cast(const Any* value) noexcept +{ + return value && value->type() == typeid(T) ? value->cast() : nullptr; +} +} // namespace util +} // namespace realm + +namespace std { +inline void swap(realm::util::Any& lhs, realm::util::Any& rhs) noexcept +{ + lhs.swap(rhs); +} +} // namespace std + +#endif // REALM_UTIL_ANY_HPP diff --git a/src/vendor-include/realm-ios/include/realm/util/assert.hpp b/src/vendor-include/realm-ios/include/realm/util/assert.hpp new file mode 100644 index 000000000..5e750664b --- /dev/null +++ b/src/vendor-include/realm-ios/include/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/src/vendor-include/realm-ios/include/realm/util/backtrace.hpp b/src/vendor-include/realm-ios/include/realm/util/backtrace.hpp new file mode 100644 index 000000000..801102392 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/backtrace.hpp @@ -0,0 +1,226 @@ +/************************************************************************* + * + * Copyright 2018 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_BACKTRACE_HPP +#define REALM_UTIL_BACKTRACE_HPP + +#include +#include +#include + +namespace realm { +namespace util { + +/// Backtrace encapsulates a stack trace, usually as captured by `backtrace()` +/// and `backtrace_symbols()` (or platform-specific equivalents). +struct Backtrace { + /// Capture a symbolicated stack trace, excluding the call to `capture()` + /// itself. If any error occurs while capturing the stack trace or + /// translating symbol names, a `Backtrace` object is returned containing a + /// single line describing the error. + /// + /// This function only allocates memory as part of calling + /// `backtrace_symbols()` (or the current platform's equivalent). + static Backtrace capture() noexcept; + + /// Print the backtrace to the stream. Each line is separated by a newline. + /// The format of the output is unspecified. + void print(std::ostream&) const; + + /// Construct an empty stack trace. + Backtrace() noexcept + { + } + + /// Move constructor. This operation cannot fail. + Backtrace(Backtrace&&) noexcept; + + /// Copy constructor. See the copy assignment operator. + Backtrace(const Backtrace&) noexcept; + + ~Backtrace(); + + /// Move assignment operator. This operation cannot fail. + Backtrace& operator=(Backtrace&&) noexcept; + + /// Copy assignment operator. Copying a `Backtrace` object may result in a + /// memory allocation. If such an allocation fails, the backtrace is + /// replaced with a single line describing the error. + Backtrace& operator=(const Backtrace&) noexcept; + +private: + Backtrace(void* memory, const char* const* strs, size_t len) + : m_memory(memory) + , m_strs(strs) + , m_len(len) + { + } + Backtrace(void* memory, size_t len) + : m_memory(memory) + , m_strs(static_cast(memory)) + , m_len(len) + { + } + + // m_memory is a pointer to the memory block returned by + // `backtrace_symbols()`. It is usually equal to `m_strs`, except in the + // case where an error has occurred and `m_strs` points to statically + // allocated memory describing the error. + // + // When `m_memory` is non-null, the memory is owned by this object. + void* m_memory = nullptr; + + // A pointer to a list of string pointers describing the stack trace (same + // format as returned by `backtrace_symbols()`). + const char* const* m_strs = nullptr; + + // Number of entries in this stack trace. + size_t m_len = 0; +}; + +namespace detail { + +class ExceptionWithBacktraceBase { +public: + ExceptionWithBacktraceBase() + : m_backtrace(util::Backtrace::capture()) + { + } + const util::Backtrace& backtrace() const noexcept + { + return m_backtrace; + } + virtual const char* message() const noexcept = 0; + +protected: + util::Backtrace m_backtrace; + // Cannot use Optional here, because Optional wants to use + // ExceptionWithBacktrace. + mutable bool m_has_materialized_message = false; + mutable std::string m_materialized_message; + + // Render the message and the backtrace into m_message_with_backtrace. If an + // exception is thrown while rendering the message, the message without the + // backtrace will be returned. + const char* materialize_message() const noexcept; +}; + +} // namespace detail + +/// Base class for exceptions that record a stack trace of where they were +/// thrown. +/// +/// The template argument is expected to be an exception type conforming to the +/// standard library exception API (`std::exception` and friends). +/// +/// It is possible to opt in to exception backtraces in two ways, (a) as part of +/// the exception type, in which case the backtrace will always be included for +/// all exceptions of that type, or (b) at the call-site of an opaque exception +/// type, in which case it is up to the throw-site to decide whether a backtrace +/// should be included. +/// +/// Example (a): +/// ``` +/// class MyException : ExceptionWithBacktrace { +/// public: +/// const char* message() const noexcept override +/// { +/// return "MyException error message"; +/// } +/// }; +/// +/// ... +/// +/// try { +/// throw MyException{}; +/// } +/// catch (const MyException& ex) { +/// // Print the backtrace without the message: +/// std::cerr << ex.backtrace() << "\n"; +/// // Print the exception message and the backtrace: +/// std::cerr << ex.what() << "\n"; +/// // Print the exception message without the backtrace: +/// std::cerr << ex.message() << "\n"; +/// } +/// ``` +/// +/// Example (b): +/// ``` +/// class MyException : std::exception { +/// public: +/// const char* what() const noexcept override +/// { +/// return "MyException error message"; +/// } +/// }; +/// +/// ... +/// +/// try { +/// throw ExceptionWithBacktrace{}; +/// } +/// catch (const MyException& ex) { +/// // Print the exception message and the backtrace: +/// std::cerr << ex.what() << "\n"; +/// } +/// ``` +template +class ExceptionWithBacktrace : public Base, public detail::ExceptionWithBacktraceBase { +public: + template + inline ExceptionWithBacktrace(Args&&... args) + : Base(std::forward(args)...) + , detail::ExceptionWithBacktraceBase() // backtrace captured here + { + } + + /// Return the message of the exception, including the backtrace of where + /// the exception was thrown. + const char* what() const noexcept final + { + return materialize_message(); + } + + /// Return the message of the exception without the backtrace. The default + /// implementation calls `Base::what()`. + const char* message() const noexcept override + { + return Base::what(); + } +}; + +// Wrappers for standard exception types with backtrace support +using runtime_error = ExceptionWithBacktrace; +using range_error = ExceptionWithBacktrace; +using overflow_error = ExceptionWithBacktrace; +using underflow_error = ExceptionWithBacktrace; +using bad_alloc = ExceptionWithBacktrace; +using invalid_argument = ExceptionWithBacktrace; +using out_of_range = ExceptionWithBacktrace; +using logic_error = ExceptionWithBacktrace; + +} // namespace util +} // namespace realm + +inline std::ostream& operator<<(std::ostream& os, const realm::util::Backtrace& bt) +{ + bt.print(os); + return os; +} + +#endif // REALM_UTIL_BACKTRACE_HPP diff --git a/src/vendor-include/realm-ios/include/realm/util/base64.hpp b/src/vendor-include/realm-ios/include/realm/util/base64.hpp new file mode 100644 index 000000000..8d808a6d7 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/base64.hpp @@ -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. + * + **************************************************************************/ + +#ifndef REALM_UTIL_BASE64_HPP +#define REALM_UTIL_BASE64_HPP + +#include +#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; +} + + + +/// base64_decode_to_vector() is a convenience function that decodes \param +/// encoded and returns the result in a std::vector with the correct size. +/// This function returns none if the input is invalid. +Optional> base64_decode_to_vector(StringData encoded); + +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_BASE64_HPP + diff --git a/src/vendor-include/realm-ios/include/realm/util/basic_system_errors.hpp b/src/vendor-include/realm-ios/include/realm/util/basic_system_errors.hpp new file mode 100644 index 000000000..8f7a626ea --- /dev/null +++ b/src/vendor-include/realm-ios/include/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/src/vendor-include/realm-ios/include/realm/util/bind_ptr.hpp b/src/vendor-include/realm-ios/include/realm/util/bind_ptr.hpp new file mode 100644 index 000000000..e5b8a99b1 --- /dev/null +++ b/src/vendor-include/realm-ios/include/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/src/vendor-include/realm-ios/include/realm/util/buffer.hpp b/src/vendor-include/realm-ios/include/realm/util/buffer.hpp new file mode 100644 index 000000000..d65bdaf44 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/buffer.hpp @@ -0,0 +1,302 @@ +/************************************************************************* + * + * 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 +#include + +namespace realm { +namespace util { + + +/// A simple buffer concept that owns a region of memory and knows its +/// size. +template +class Buffer { +public: + Buffer(Allocator& alloc = Allocator::get_default()) noexcept + : m_data(nullptr, STLDeleter{alloc}) + , m_size(0) + { + } + explicit Buffer(size_t initial_size, Allocator& alloc = Allocator::get_default()); + Buffer(Buffer&&) noexcept = default; + 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); + + /// Release the internal buffer to the caller. + REALM_NODISCARD std::unique_ptr> 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); + } + + Allocator& get_allocator() const noexcept + { + return m_data.get_deleter().get_allocator(); + } + +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(Allocator& alloc = Allocator::get_default()) 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. + REALM_NODISCARD 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, A& alloc) + : m_data(util::make_unique(alloc, initial_size)) // Throws + , m_size(initial_size) +{ +} + +template +inline void Buffer::set_size(size_t new_size) +{ + m_data = util::make_unique(get_allocator(), 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) +{ + auto new_data = util::make_unique(get_allocator(), new_size); // Throws + realm::safe_copy_n(m_data.get() + copy_begin, copy_end - copy_begin, new_data.get() + copy_to); + m_data = std::move(new_data); + 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 std::unique_ptr> Buffer::release() noexcept +{ + m_size = 0; + return std::move(m_data); +} + + +template +inline AppendBuffer::AppendBuffer(A& alloc) noexcept + : m_buffer(alloc) + , 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/src/vendor-include/realm-ios/include/realm/util/buffer_stream.hpp b/src/vendor-include/realm-ios/include/realm/util/buffer_stream.hpp new file mode 100644 index 000000000..be5064e39 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/buffer_stream.hpp @@ -0,0 +1,151 @@ +/************************************************************************* + * + * 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 +#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::size_t 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::size_t 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::size_t BasicResettableExpandableOutputStreambuf::size() const noexcept +{ + std::size_t size = std::size_t(this->pptr() - this->pbase()); + return size; +} + +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::size_t BasicResettableExpandableBufferOutputStream::size() const noexcept +{ + return m_streambuf.size(); +} + +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_BUFFER_STREAM_HPP diff --git a/src/vendor-include/realm-ios/include/realm/util/call_with_tuple.hpp b/src/vendor-include/realm-ios/include/realm/util/call_with_tuple.hpp new file mode 100644 index 000000000..7d2eab06b --- /dev/null +++ b/src/vendor-include/realm-ios/include/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/src/vendor-include/realm-ios/include/realm/util/cf_ptr.hpp b/src/vendor-include/realm-ios/include/realm/util/cf_ptr.hpp new file mode 100644 index 000000000..a1ec431f1 --- /dev/null +++ b/src/vendor-include/realm-ios/include/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/src/vendor-include/realm-ios/include/realm/util/cf_str.hpp b/src/vendor-include/realm-ios/include/realm/util/cf_str.hpp new file mode 100644 index 000000000..a1ae1ec71 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/cf_str.hpp @@ -0,0 +1,54 @@ +/************************************************************************* + * + * Copyright 2020 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_STR_HPP +#define REALM_UTIL_CF_STR_HPP + +#include + +#if REALM_PLATFORM_APPLE + +#include + +namespace realm { +namespace util { + +inline std::string cfstring_to_std_string(CFStringRef cf_str) +{ + std::string ret; + // If the CFString happens to store UTF-8 we can read its data directly + if (const char *utf8 = CFStringGetCStringPtr(cf_str, kCFStringEncodingUTF8)) { + ret = utf8; + return ret; + } + + // Otherwise we need to convert the CFString to UTF-8 + CFIndex length = CFStringGetLength(cf_str); + CFIndex max_size = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1; + ret.resize(max_size); + CFStringGetCString(cf_str, &ret[0], max_size, kCFStringEncodingUTF8); + ret.resize(strlen(ret.c_str())); + return ret; +} + +} +} + +#endif // REALM_PLATFORM_APPLE + +#endif // REALM_UTIL_CF_STR_HPP diff --git a/src/vendor-include/realm-ios/include/realm/util/circular_buffer.hpp b/src/vendor-include/realm-ios/include/realm/util/circular_buffer.hpp new file mode 100644 index 000000000..4ae07cc75 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/circular_buffer.hpp @@ -0,0 +1,1011 @@ +/************************************************************************* + * + * 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_CIRCULAR_BUFFER_HPP +#define REALM_UTIL_CIRCULAR_BUFFER_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace realm { +namespace util { + +/// \brief A container backed by a "circular buffer". +/// +/// This container is similar to std::deque in that it offers efficient element +/// insertion and removal at both ends. Insertion at either end occurs in +/// amortized constant time. Removal at either end occurs in constant time. +/// +/// As opposed to std::deque, this container allows for reservation of buffer +/// space, such that value insertion can be guaranteed to not reallocate buffer +/// memory, and to not throw. +/// +/// More specifically, a single insert operation, that inserts zero or more +/// values at either end, is guaranteed to not reallocate buffer memory if the +/// prior capacity (capacity()) is greater than, or equal to the prior size +/// (size()) plus the number of inserted values. Further more, such an operation +/// is guaranteed to not throw if the capacity is sufficient, and the relevant +/// constructor of the value type does not throw, and, in the case of inserting +/// a range of values specified as a pair of iterators, if no exception is +/// thrown while operating on those iterators. +/// +/// This container uses a single contiguous chunk of memory as backing storage, +/// but it allows for the logical sequence of values to wrap around from the +/// end, to the beginning of that chunk. Because the logical sequence of values +/// can have a storage-wise discontinuity of this kind, this container does not +/// meet the requirements of `ContiguousContainer` (as defined by C++17). +/// +/// When the first element is removed (pop_front()), iterators pointing to the +/// removed element will be invalidated. All other iterators, including "end +/// iterators" (end()), will remain valid. +/// +/// When the last element is removed (pop_back()), iterators pointing to the +/// removed element will become "end iterators" (end()), and "end iterators" +/// will be invalidated. All other iterators will remain valid. +/// +/// When an element is inserted at the front (push_front()), and the prior +/// capacity (capacity()) is strictly greater than the prior size (size()), all +/// iterators remain valid. +/// +/// When an element is inserted at the back (push_back()), and the prior +/// capacity (capacity()) is strictly greater than the prior size (size()), "end +/// iterators" (end()) become iterators to the inserted element, and all other +/// iterators remain valid. +/// +/// Operations pop_front(), pop_back(), and clear(), are guaranteed to leave the +/// capacity unchanged. +/// +/// Iterators are of the "random access" kind (std::random_access_iterator_tag). +template class CircularBuffer { +private: + template class Iter; + + template using RequireIter = + std::enable_if_t::iterator_category, + std::input_iterator_tag>::value>; + +public: + static_assert(std::is_nothrow_destructible::value, ""); + + using value_type = T; + using size_type = std::size_t; + using reference = value_type&; + using const_reference = const value_type&; + using iterator = Iter; + using const_iterator = Iter; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + + CircularBuffer() noexcept; + CircularBuffer(const CircularBuffer&); + CircularBuffer(CircularBuffer&&) noexcept; + CircularBuffer(std::initializer_list); + explicit CircularBuffer(size_type size); + CircularBuffer(size_type size, const T& value); + template> CircularBuffer(I begin, I end); + ~CircularBuffer() noexcept; + + CircularBuffer& operator=(const CircularBuffer&); + CircularBuffer& operator=(CircularBuffer&&) noexcept; + CircularBuffer& operator=(std::initializer_list); + + void assign(std::initializer_list); + void assign(size_type size, const T& value); + template> void assign(I begin, I end); + + // Element access + + reference at(size_type); + const_reference at(size_type) const; + + reference operator[](size_type) noexcept; + const_reference operator[](size_type) const noexcept; + + reference front() noexcept; + const_reference front() const noexcept; + + reference back() noexcept; + const_reference back() const noexcept; + + // Iterators + + iterator begin() noexcept; + const_iterator begin() const noexcept; + const_iterator cbegin() const noexcept; + + iterator end() noexcept; + const_iterator end() const noexcept; + const_iterator cend() const noexcept; + + reverse_iterator rbegin() noexcept; + const_reverse_iterator rbegin() const noexcept; + const_reverse_iterator crbegin() const noexcept; + + reverse_iterator rend() noexcept; + const_reverse_iterator rend() const noexcept; + const_reverse_iterator crend() const noexcept; + + // Size / capacity + + bool empty() const noexcept; + size_type size() const noexcept; + size_type capacity() const noexcept; + + void reserve(size_type capacity); + void shrink_to_fit(); + + // Modifiers + + reference push_front(const T&); + reference push_back(const T&); + + reference push_front(T&&); + reference push_back(T&&); + + template reference emplace_front(Args&&...); + template reference emplace_back(Args&&...); + + void pop_front() noexcept; + void pop_back() noexcept; + + // FIXME: emplace(const_iterator i, ...) -> j = unwrap(i.m_index); if (j >= (m_size+1)/2) insert_near_back(j, ...); else insert_near_front(j, ...); + + void clear() noexcept; + void resize(size_type size); + void resize(size_type size, const T& value); + + void swap(CircularBuffer&) noexcept; + + // Comparison + + template bool operator==(const CircularBuffer&) const + noexcept(noexcept(std::declval() == std::declval())); + template bool operator!=(const CircularBuffer&) const + noexcept(noexcept(std::declval() == std::declval())); + template bool operator<(const CircularBuffer&) const + noexcept(noexcept(std::declval() < std::declval())); + template bool operator>(const CircularBuffer&) const + noexcept(noexcept(std::declval() < std::declval())); + template bool operator<=(const CircularBuffer&) const + noexcept(noexcept(std::declval() < std::declval())); + template bool operator>=(const CircularBuffer&) const + noexcept(noexcept(std::declval() < std::declval())); + +private: + using Strut = typename std::aligned_storage::type; + std::unique_ptr m_memory_owner; + + // Index of first element in allocated memory chunk. + size_type m_begin = 0; + + // The number of elements within the allocated memory chunk, that are + // currently in use, i.e., the logical size of the circular buffer. + size_type m_size = 0; + + // Number of elements of type T that will fit into the currently allocated + // memory chunk. + // + // Except when m_size is zero, m_allocated_size must be strictly greater + // than m_size. This is required to ensure that the iterators returned by + // begin() and end() are equal only when the buffer is empty. + // + // INVARIANT: m_size == 0 ? m_allocated_size == 0 : m_size < m_allocated_size + size_type m_allocated_size = 0; + + T* get_memory_ptr() noexcept; + + // Assumption: index < m_allocated_size + size_type circular_inc(size_type index) noexcept; + size_type circular_dec(size_type index) noexcept; + size_type wrap(size_type index) noexcept; + size_type unwrap(size_type index) noexcept; + + template void copy(I begin, I end); + template void copy(I begin, I end, std::input_iterator_tag); + template void copy(I begin, I end, std::forward_iterator_tag); + + void destroy(size_type offset = 0) noexcept; + + void realloc(size_type new_allocated_size); +}; + + +template void swap(CircularBuffer&, CircularBuffer&) noexcept; + + + + +// Implementation + +template template class CircularBuffer::Iter : + public std::iterator { +public: + using difference_type = std::ptrdiff_t; + + Iter() noexcept + { + } + + template Iter(const Iter& i) noexcept + { + operator=(i); + } + + template Iter& operator=(const Iter& i) noexcept + { + // Check constness convertability + static_assert(std::is_convertible::value, ""); + m_buffer = i.m_buffer; + m_index = i.m_index; + return *this; + } + + U& operator*() const noexcept + { + T* memory = m_buffer->get_memory_ptr(); + return memory[m_index]; + } + + U* operator->() const noexcept + { + return &operator*(); + } + + U& operator[](difference_type i) const noexcept + { + Iter j = *this; + j += i; + return *j; + } + + Iter& operator++() noexcept + { + m_index = m_buffer->circular_inc(m_index); + return *this; + } + + Iter& operator--() noexcept + { + m_index = m_buffer->circular_dec(m_index); + return *this; + } + + Iter operator++(int) noexcept + { + size_type i = m_index; + operator++(); + return Iter{m_buffer, i}; + } + + Iter operator--(int) noexcept + { + size_type i = m_index; + operator--(); + return Iter{m_buffer, i}; + } + + Iter& operator+=(difference_type value) noexcept + { + // Care is needed to avoid unspecified arithmetic behaviour here. We can + // assume, however, that if `i` is the unwrapped (logical) index of the + // element pointed to by this iterator, then the mathematical value of i + // + value is representable in `size_type` (otherwise the resulting + // iterator would escape the boundaries of the buffer). We can therefore + // safely perform the addition in the unsigned domain of unwrapped + // element indexes, and rely on two's complement representation for + // negative values. + size_type i = m_buffer->unwrap(m_index); + i += size_type(value); + m_index = m_buffer->wrap(i); + return *this; + } + + Iter& operator-=(difference_type value) noexcept + { + // Care is needed to avoid unspecified arithmetic behaviour here. See + // the comment in the implementation of operator+=(). + size_type i = m_buffer->unwrap(m_index); + i -= size_type(value); + m_index = m_buffer->wrap(i); + return *this; + } + + Iter operator+(difference_type value) const noexcept + { + Iter i = *this; + i += value; + return i; + } + + Iter operator-(difference_type value) const noexcept + { + Iter i = *this; + i -= value; + return i; + } + + friend Iter operator+(difference_type value, const Iter& i) noexcept + { + Iter j = i; + j += value; + return j; + } + + template difference_type operator-(const Iter& i) const noexcept + { + REALM_ASSERT(m_buffer == i.m_buffer); + size_type i_1 = m_buffer->unwrap(m_index); + size_type i_2 = i.m_buffer->unwrap(i.m_index); + return from_twos_compl(size_type(i_1 - i_2)); + } + + template bool operator==(const Iter& i) const noexcept + { + REALM_ASSERT(m_buffer == i.m_buffer); + return (m_index == i.m_index); + } + + template bool operator!=(const Iter& i) const noexcept + { + return !operator==(i); + } + + template bool operator<(const Iter& i) const noexcept + { + REALM_ASSERT(m_buffer == i.m_buffer); + size_type i_1 = m_buffer->unwrap(m_index); + size_type i_2 = i.m_buffer->unwrap(i.m_index); + return (i_1 < i_2); + } + + template bool operator>(const Iter& i) const noexcept + { + return (i < *this); + } + + template bool operator<=(const Iter& i) const noexcept + { + return !operator>(i); + } + + template bool operator>=(const Iter& i) const noexcept + { + return !operator<(i); + } + +private: + CircularBuffer* m_buffer = nullptr; + + // Index of iterator position from beginning of allocated memory, i.e., from + // beginning of m_buffer->get_memory_ptr(). + size_type m_index = 0; + + Iter(CircularBuffer* buffer, size_type index) noexcept : + m_buffer{buffer}, + m_index{index} + { + } + + friend class CircularBuffer; + template friend class Iter; +}; + +template inline CircularBuffer::CircularBuffer() noexcept +{ +} + +template inline CircularBuffer::CircularBuffer(const CircularBuffer& buffer) +{ + try { + copy(buffer.begin(), buffer.end()); // Throws + } + catch (...) { + // If an exception was thrown above, the destructor will not be called, + // so we need to manually destroy the copies that were already made. + destroy(); + throw; + } +} + +template inline CircularBuffer::CircularBuffer(CircularBuffer&& buffer) noexcept : + m_memory_owner{std::move(buffer.m_memory_owner)}, + m_begin{buffer.m_begin}, + m_size{buffer.m_size}, + m_allocated_size{buffer.m_allocated_size} +{ + buffer.m_begin = 0; + buffer.m_size = 0; + buffer.m_allocated_size = 0; +} + +template inline CircularBuffer::CircularBuffer(std::initializer_list list) +{ + try { + copy(list.begin(), list.end()); // Throws + } + catch (...) { + // If an exception was thrown above, the destructor will not be called, + // so we need to manually destroy the copies that were already made. + destroy(); + throw; + } +} + +template inline CircularBuffer::CircularBuffer(size_type count) +{ + try { + resize(count); // Throws + } + catch (...) { + // If an exception was thrown above, the destructor will not be called, + // so we need to manually destroy the instances that were already + // created. + destroy(); + throw; + } +} + +template inline CircularBuffer::CircularBuffer(size_type count, const T& value) +{ + try { + resize(count, value); // Throws + } + catch (...) { + // If an exception was thrown above, the destructor will not be called, + // so we need to manually destroy the copies that were already made. + destroy(); + throw; + } +} + +template template inline CircularBuffer::CircularBuffer(I begin, I end) +{ + try { + copy(begin, end); // Throws + } + catch (...) { + // If an exception was thrown above, the destructor will not be called, + // so we need to manually destroy the copies that were already made. + destroy(); + throw; + } +} + +template inline CircularBuffer::~CircularBuffer() noexcept +{ + destroy(); +} + +template +inline auto CircularBuffer::operator=(const CircularBuffer& buffer) -> CircularBuffer& +{ + clear(); + copy(buffer.begin(), buffer.end()); // Throws + return *this; +} + +template +inline auto CircularBuffer::operator=(CircularBuffer&& buffer) noexcept -> CircularBuffer& +{ + destroy(); + m_memory_owner = std::move(buffer.m_memory_owner); + m_begin = buffer.m_begin; + m_size = buffer.m_size; + m_allocated_size = buffer.m_allocated_size; + buffer.m_begin = 0; + buffer.m_size = 0; + buffer.m_allocated_size = 0; + return *this; +} + +template +inline auto CircularBuffer::operator=(std::initializer_list list) -> CircularBuffer& +{ + clear(); + copy(list.begin(), list.end()); // Throws + return *this; +} + +template inline void CircularBuffer::assign(std::initializer_list list) +{ + clear(); + copy(list.begin(), list.end()); // Throws +} + +template inline void CircularBuffer::assign(size_type count, const T& value) +{ + clear(); + resize(count, value); // Throws +} + +template template inline void CircularBuffer::assign(I begin, I end) +{ + clear(); + copy(begin, end); // Throws +} + +template inline auto CircularBuffer::at(size_type i) -> reference +{ + if (REALM_LIKELY(i < m_size)) + return operator[](i); + throw util::out_of_range{"Index"}; +} + +template inline auto CircularBuffer::at(size_type i) const -> const_reference +{ + return const_cast(this)->at(i); // Throws +} + +template +inline auto CircularBuffer::operator[](size_type i) noexcept -> reference +{ + REALM_ASSERT(i < m_size); + T* memory = get_memory_ptr(); + size_type j = wrap(i); + return memory[j]; +} + +template +inline auto CircularBuffer::operator[](size_type i) const noexcept -> const_reference +{ + return const_cast(this)->operator[](i); +} + +template inline auto CircularBuffer::front() noexcept -> reference +{ + return operator[](0); +} + +template inline auto CircularBuffer::front() const noexcept -> const_reference +{ + return operator[](0); +} + +template inline auto CircularBuffer::back() noexcept -> reference +{ + return operator[](m_size-1); +} + +template +inline auto CircularBuffer::back() const noexcept -> const_reference +{ + return operator[](m_size-1); +} + +template inline auto CircularBuffer::begin() noexcept -> iterator +{ + return iterator{this, m_begin}; +} + +template inline auto CircularBuffer::begin() const noexcept -> const_iterator +{ + return const_cast(this)->begin(); +} + +template inline auto CircularBuffer::cbegin() const noexcept -> const_iterator +{ + return begin(); +} + +template inline auto CircularBuffer::end() noexcept -> iterator +{ + size_type i = wrap(m_size); + return iterator{this, i}; +} + +template inline auto CircularBuffer::end() const noexcept -> const_iterator +{ + return const_cast(this)->end(); +} + +template inline auto CircularBuffer::cend() const noexcept -> const_iterator +{ + return end(); +} + +template inline auto CircularBuffer::rbegin() noexcept -> reverse_iterator +{ + return std::reverse_iterator(end()); +} + +template inline auto CircularBuffer::rbegin() const noexcept -> const_reverse_iterator +{ + return const_cast(this)->rbegin(); +} + +template inline auto CircularBuffer::crbegin() const noexcept -> const_reverse_iterator +{ + return rbegin(); +} + +template inline auto CircularBuffer::rend() noexcept -> reverse_iterator +{ + return std::reverse_iterator(begin()); +} + +template inline auto CircularBuffer::rend() const noexcept -> const_reverse_iterator +{ + return const_cast(this)->rend(); +} + +template inline auto CircularBuffer::crend() const noexcept -> const_reverse_iterator +{ + return rend(); +} + +template inline bool CircularBuffer::empty() const noexcept +{ + return (m_size == 0); +} + +template inline auto CircularBuffer::size() const noexcept -> size_type +{ + return m_size; +} + +template void CircularBuffer::reserve(size_type capacity) +{ + if (capacity == 0) + return; + + // An extra element of capacity is needed such that the end iterator can + // always point one beyond the last element without becomeing equal to an + // iterator to the first element. + size_type min_allocated_size = capacity; + if (REALM_UNLIKELY(int_add_with_overflow_detect(min_allocated_size, 1))) + throw util::overflow_error{"Capacity"}; + + if (min_allocated_size <= m_allocated_size) + return; + + size_type new_allocated_size = m_allocated_size; + if (REALM_UNLIKELY(int_multiply_with_overflow_detect(new_allocated_size, 2))) + new_allocated_size = std::numeric_limits::max(); + if (new_allocated_size < min_allocated_size) + new_allocated_size = min_allocated_size; + realloc(new_allocated_size); // Throws +} + +template inline void CircularBuffer::shrink_to_fit() +{ + if (m_size > 0) { + // An extra element of capacity is needed such that the end iterator can + // always point one beyond the last element without becomeing equal to + // an iterator to the first element. + size_type new_allocated_size = m_size + 1; + if (new_allocated_size < m_allocated_size) + realloc(new_allocated_size); // Throws + } + else { + m_memory_owner.reset(); + m_begin = 0; + m_allocated_size = 0; + } +} + +template inline auto CircularBuffer::capacity() const noexcept -> size_type +{ + return (m_allocated_size > 0 ? m_allocated_size - 1 : 0); +} + +template inline auto CircularBuffer::push_front(const T& value) -> reference +{ + return emplace_front(value); // Throws +} + +template inline auto CircularBuffer::push_back(const T& value) -> reference +{ + return emplace_back(value); // Throws +} + +template inline auto CircularBuffer::push_front(T&& value) -> reference +{ + return emplace_front(value); // Throws +} + +template inline auto CircularBuffer::push_back(T&& value) -> reference +{ + return emplace_back(value); // Throws +} + +template +template inline auto CircularBuffer::emplace_front(Args&&... args) -> reference +{ + size_type new_size = m_size + 1; + reserve(new_size); // Throws + REALM_ASSERT(m_allocated_size > 0); + T* memory = get_memory_ptr(); + size_type i = circular_dec(m_begin); + new (&memory[i]) T(std::forward(args)...); // Throws + m_begin = i; + m_size = new_size; + return memory[i]; +} + +template +template inline auto CircularBuffer::emplace_back(Args&&... args) -> reference +{ + size_type new_size = m_size + 1; + reserve(new_size); // Throws + REALM_ASSERT(m_allocated_size > 0); + T* memory = get_memory_ptr(); + size_type i = wrap(m_size); + new (&memory[i]) T(std::forward(args)...); // Throws + m_size = new_size; + return memory[i]; +} + +template inline void CircularBuffer::pop_front() noexcept +{ + REALM_ASSERT(m_size > 0); + T* memory = get_memory_ptr(); + memory[m_begin].~T(); + m_begin = circular_inc(m_begin); + --m_size; +} + +template inline void CircularBuffer::pop_back() noexcept +{ + REALM_ASSERT(m_size > 0); + T* memory = get_memory_ptr(); + size_type new_size = m_size - 1; + size_type i = wrap(new_size); + memory[i].~T(); + m_size = new_size; +} + +template inline void CircularBuffer::clear() noexcept +{ + destroy(); + m_begin = 0; + m_size = 0; +} + +template inline void CircularBuffer::resize(size_type size) +{ + if (size <= m_size) { + size_type offset = size; + destroy(offset); + m_size = size; + return; + } + reserve(size); // Throws + T* memory = get_memory_ptr(); + size_type i = wrap(m_size); + do { + new (&memory[i]) T(); // Throws + i = circular_inc(i); + ++m_size; + } + while (m_size < size); +} + +template inline void CircularBuffer::resize(size_type size, const T& value) +{ + if (size <= m_size) { + size_type offset = size; + destroy(offset); + m_size = size; + return; + } + reserve(size); // Throws + T* memory = get_memory_ptr(); + size_type i = wrap(m_size); + do { + new (&memory[i]) T(value); // Throws + i = circular_inc(i); + ++m_size; + } + while (m_size < size); +} + +template inline void CircularBuffer::swap(CircularBuffer& buffer) noexcept +{ + std::swap(m_memory_owner, buffer.m_memory_owner); + std::swap(m_begin, buffer.m_begin); + std::swap(m_size, buffer.m_size); + std::swap(m_allocated_size, buffer.m_allocated_size); +} + +template template +inline bool CircularBuffer::operator==(const CircularBuffer& buffer) const + noexcept(noexcept(std::declval() == std::declval())) +{ + return std::equal(begin(), end(), buffer.begin(), buffer.end()); // Throws +} + +template template +inline bool CircularBuffer::operator!=(const CircularBuffer& buffer) const + noexcept(noexcept(std::declval() == std::declval())) +{ + return !operator==(buffer); // Throws +} + +template template +inline bool CircularBuffer::operator<(const CircularBuffer& buffer) const + noexcept(noexcept(std::declval() < std::declval())) +{ + return std::lexicographical_compare(begin(), end(), buffer.begin(), buffer.end()); // Throws +} + +template template +inline bool CircularBuffer::operator>(const CircularBuffer& buffer) const + noexcept(noexcept(std::declval() < std::declval())) +{ + return (buffer < *this); // Throws +} + +template template +inline bool CircularBuffer::operator<=(const CircularBuffer& buffer) const + noexcept(noexcept(std::declval() < std::declval())) +{ + return !operator>(buffer); // Throws +} + +template template +inline bool CircularBuffer::operator>=(const CircularBuffer& buffer) const + noexcept(noexcept(std::declval() < std::declval())) +{ + return !operator<(buffer); // Throws +} + +template inline T* CircularBuffer::get_memory_ptr() noexcept +{ + return static_cast(static_cast(m_memory_owner.get())); +} + +template +inline auto CircularBuffer::circular_inc(size_type index) noexcept -> size_type +{ + size_type index_2 = index + 1; + if (REALM_LIKELY(index_2 < m_allocated_size)) + return index_2; + return 0; +} + +template +inline auto CircularBuffer::circular_dec(size_type index) noexcept -> size_type +{ + if (REALM_LIKELY(index > 0)) + return index - 1; + return m_allocated_size - 1; +} + +template +inline auto CircularBuffer::wrap(size_type index) noexcept -> size_type +{ + size_type top = m_allocated_size - m_begin; + if (index < top) + return m_begin + index; + return index - top; +} + +template +inline auto CircularBuffer::unwrap(size_type index) noexcept -> size_type +{ + if (index >= m_begin) + return index - m_begin; + return m_allocated_size - (m_begin - index); +} + +template template inline void CircularBuffer::copy(I begin, I end) +{ + using iterator_category = typename std::iterator_traits::iterator_category; + copy(begin, end, iterator_category{}); // Throws +} + +template template +inline void CircularBuffer::copy(I begin, I end, std::input_iterator_tag) +{ + for (I j = begin; j != end; ++j) + push_back(*j); // Throws +} + +template template +inline void CircularBuffer::copy(I begin, I end, std::forward_iterator_tag) +{ + REALM_ASSERT(m_begin == 0); + REALM_ASSERT(m_size == 0); + size_type size = std::distance(begin, end); + reserve(size); // Throws + T* memory = get_memory_ptr(); + for (I i = begin; i != end; ++i) { + new (&memory[m_size]) T(*i); // Throws + ++m_size; + } +} + +template inline void CircularBuffer::destroy(size_type offset) noexcept +{ + T* memory = get_memory_ptr(); + size_type j = m_begin; + for (size_type i = offset; i < m_size; ++i) { + memory[j].~T(); + j = circular_inc(j); + } +} + +template void CircularBuffer::realloc(size_type new_allocated_size) +{ + REALM_ASSERT(new_allocated_size > 1); + REALM_ASSERT(new_allocated_size > m_size); + + // Allocate new buffer + std::unique_ptr new_memory_owner = + std::make_unique(new_allocated_size); // Throws + T* memory = get_memory_ptr(); + + // Move or copy elements to new buffer + { + T* new_memory = static_cast(static_cast(new_memory_owner.get())); + size_type i = 0; + try { + size_type j = m_begin; + while (i < m_size) { + new (&new_memory[i]) T(std::move_if_noexcept(memory[j])); // Throws + ++i; + j = circular_inc(j); + } + } + catch (...) { + // If an exception was thrown above, we know that elements were + // copied, and not moved (assuming that T is copy constructable if + // it is not nothrow move constructible), so we need to back out by + // destroying the copies that were already made. + for (size_type j = 0; j < i; ++j) + new_memory[j].~T(); + throw; + } + } + + // Destroy old elements + { + size_type j = m_begin; + for (size_type i = 0; i < m_size; ++i) { + memory[j].~T(); + j = circular_inc(j); + } + } + + m_memory_owner = std::move(new_memory_owner); + m_begin = 0; + m_allocated_size = new_allocated_size; +} + +template inline void swap(CircularBuffer& a, CircularBuffer& b) noexcept +{ + a.swap(b); +} + +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_CIRCULAR_BUFFER_HPP + diff --git a/src/vendor-include/realm-ios/include/realm/util/config.h b/src/vendor-include/realm-ios/include/realm/util/config.h new file mode 100644 index 000000000..0ebbbe27b --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/config.h @@ -0,0 +1,23 @@ +// Version information +#define REALM_VERSION "" + +// Specific headers +#define HAVE_MALLOC_H 0 + +// Realm-specific configuration +#define REALM_MAX_BPNODE_SIZE 1000 +#define REALM_ENABLE_ASSERTIONS 1 +#define REALM_ENABLE_ALLOC_SET_ZERO 0 +#define REALM_ENABLE_ENCRYPTION 1 +#define REALM_ENABLE_MEMDEBUG 0 +#define REALM_VALGRIND 0 +#define REALM_METRICS 1 +#define REALM_ASAN 0 +#define REALM_TSAN 0 + +#define REALM_INSTALL_PREFIX "/usr/local" +#define REALM_INSTALL_INCLUDEDIR "include" +#define REALM_INSTALL_BINDIR "bin" +#define REALM_INSTALL_LIBDIR "lib" +#define REALM_INSTALL_LIBEXECDIR "libexec" +#define REALM_INSTALL_EXEC_PREFIX "/usr/local" diff --git a/src/vendor-include/realm-ios/include/realm/util/copy_dir_recursive.hpp b/src/vendor-include/realm-ios/include/realm/util/copy_dir_recursive.hpp new file mode 100644 index 000000000..a53ccfff6 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/copy_dir_recursive.hpp @@ -0,0 +1,48 @@ +/************************************************************************* + * + * 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_COPY_DIR_RECURSIVE_HPP +#define REALM_UTIL_COPY_DIR_RECURSIVE_HPP + +#include + +namespace realm { +namespace util { + +/// Recursively copy the specified directory. +/// +/// It is not an error if the target directory already exists. It is also not an +/// error if the target directory is not empty. If the origin and target +/// directories contain a file of the same name, the one in the target directory +/// will be overwitten. Other files that already exist in the target directory +/// will be left alone. +/// +/// \param skip_special_files If true, entries in the origin directory that are +/// neither regular files nor subdirectories will be skipped (not copied). If +/// false (the default), this function will fail if such an entry is encoutered. +/// +/// FIXME: This function ought to be moved to in the +/// realm-core repository. +void copy_dir_recursive(const std::string& origin_path, const std::string& target_path, + bool skip_special_files = false); + +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_COPY_DIR_RECURSIVE_HPP diff --git a/src/vendor-include/realm-ios/include/realm/util/demangle.hpp b/src/vendor-include/realm-ios/include/realm/util/demangle.hpp new file mode 100644 index 000000000..7af4fb84e --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/demangle.hpp @@ -0,0 +1,54 @@ +/************************************************************************* + * + * 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_DEMANGLE_HPP +#define REALM_UTIL_DEMANGLE_HPP + +#include +#include + +namespace realm { +namespace util { + + +/// Demangle the specified C++ ABI identifier. +/// +/// See for example +/// http://gcc.gnu.org/onlinedocs/libstdc++/latest-doxygen/namespaceabi.html +std::string demangle(const std::string&); + + +/// Get the demangled name of the specified type. +template inline std::string get_type_name() +{ + return demangle(typeid(T).name()); +} + + +/// Get the demangled name of the type of the specified argument. +template inline std::string get_type_name(const T& v) +{ + return demangle(typeid(v).name()); +} + + +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_DEMANGLE_HPP diff --git a/src/vendor-include/realm-ios/include/realm/util/duplicating_logger.hpp b/src/vendor-include/realm-ios/include/realm/util/duplicating_logger.hpp new file mode 100644 index 000000000..774df3cc8 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/duplicating_logger.hpp @@ -0,0 +1,63 @@ +/************************************************************************* + * + * 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_DUPLICATING_LOGGER_HPP +#define REALM_UTIL_DUPLICATING_LOGGER_HPP + +#include + + +namespace realm { +namespace util { + +/// The log level threshold of a logger of this type will be decided by the +/// associated base logger. Therefore, the log level threshold specified via the +/// auxiliary logger will be ignored. +/// +/// Loggers of this type are thread-safe if the base logger and the auxiliary +/// loggers are both thread-safe. +class DuplicatingLogger : public Logger { +public: + explicit DuplicatingLogger(Logger& base_logger, Logger& aux_logger) noexcept; + +protected: + void do_log(Logger::Level, std::string message) override; + +private: + Logger& m_base_logger; + Logger& m_aux_logger; +}; + + + + +// Implementation + +inline DuplicatingLogger::DuplicatingLogger(Logger& base_logger, Logger& aux_logger) noexcept : + Logger{base_logger.level_threshold}, + m_base_logger{base_logger}, m_aux_logger{aux_logger} +{ +} + + +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_DUPLICATING_LOGGER_HPP diff --git a/src/vendor-include/realm-ios/include/realm/util/encrypted_file_mapping.hpp b/src/vendor-include/realm-ios/include/realm/util/encrypted_file_mapping.hpp new file mode 100644 index 000000000..6f5fd3c0d --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/encrypted_file_mapping.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_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, 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); + + size_t collect_decryption_count() + { + return m_num_decrypted; + } + // reclaim any untouched pages - this is thread safe with respect to + // concurrent access/touching of pages - but must be called with the mutex locked. + void reclaim_untouched(size_t& progress_ptr, size_t& accumulated_savings) noexcept; + + bool contains_page(size_t page_in_file) const; + size_t get_local_index_of_address(const void* addr, size_t offset = 0) const; + + size_t get_end_index() + { + return m_first_page + m_page_state.size(); + } + size_t get_start_index() + { + return m_first_page; + } + +private: + SharedFileInfo& m_file; + + size_t m_page_shift; + size_t m_blocks_per_page; + + void* m_addr = nullptr; + + size_t m_first_page; + size_t m_num_decrypted; // 1 for every page decrypted + + enum PageState { + Touched = 1, // a ref->ptr translation has taken place + UpToDate = 2, // the page is fully up to date + PartiallyUpToDate = 4, // the page is valid for old translations, but requires re-decryption for new + Dirty = 8 // the page has been modified with respect to what's on file. + }; + std::vector m_page_state; + // little helpers: + inline void clear(PageState& ps, int p) + { + ps = PageState(ps & ~p); + } + inline bool is_not(PageState& ps, int p) + { + return (ps & p) == 0; + } + inline bool is(PageState& ps, int p) + { + return (ps & p) != 0; + } + inline void set(PageState& ps, int p) + { + ps = PageState(ps | p); + } + // 1K pages form a chunk - this array allows us to skip entire chunks during scanning + std::vector m_chunk_dont_scan; + static constexpr int page_to_chunk_shift = 10; + static constexpr size_t page_to_chunk_factor = size_t(1) << page_to_chunk_shift; + + File::AccessMode m_access; + +#ifdef REALM_DEBUG + std::unique_ptr m_validate_buffer; +#endif + + char* page_addr(size_t local_page_ndx) const noexcept; + + void mark_outdated(size_t local_page_ndx) noexcept; + bool copy_up_to_date_page(size_t local_page_ndx) noexcept; + void refresh_page(size_t local_page_ndx); + void write_page(size_t local_page_ndx) noexcept; + void write_and_update_all(size_t local_page_ndx, size_t begin_offset, size_t end_offset) noexcept; + void reclaim_page(size_t page_ndx); + void validate_page(size_t local_page_ndx) noexcept; + void validate() noexcept; +}; + +inline size_t EncryptedFileMapping::get_local_index_of_address(const void* addr, size_t offset) const +{ + REALM_ASSERT_EX(addr >= m_addr, addr, m_addr); + + size_t local_ndx = ((reinterpret_cast(addr) - reinterpret_cast(m_addr) + offset) >> m_page_shift); + REALM_ASSERT_EX(local_ndx < m_page_state.size(), local_ndx, m_page_state.size()); + return local_ndx; +} + +inline bool EncryptedFileMapping::contains_page(size_t page_in_file) const +{ + // first check for (page_in_file >= m_first_page) so that the following + // subtraction using unsigned types never wraps under 0 + return page_in_file >= m_first_page && page_in_file - m_first_page < m_page_state.size(); +} + + +} +} + +#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/src/vendor-include/realm-ios/include/realm/util/enum.hpp b/src/vendor-include/realm-ios/include/realm/util/enum.hpp new file mode 100644 index 000000000..c11d166a7 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/enum.hpp @@ -0,0 +1,224 @@ +/************************************************************************* + * + * 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_ENUM_HPP +#define REALM_UTIL_ENUM_HPP + +#include +#include +#include +#include + + +namespace realm { +namespace util { + +/// This template class allows you to endow a fundamental `enum` type with +/// information about how to print out the individual values, and how to parse +/// them. +/// +/// Here is an example: +/// +/// // Declaration +/// +/// enum class Color { orange, purple, brown }; +/// +/// struct ColorSpec { static EnumAssoc map[]; }; +/// using ColorEnum = Enum; +/// +/// // Implementation +/// +/// EnumAssoc ColorSpec::map[] = { +/// { int(Color::orange), "orange" }, +/// { int(Color::purple), "purple" }, +/// { int(Color::brown), "brown" }, +/// { 0, 0 } +/// }; +/// +/// // Application +/// +/// ColorEnum color = Color::purple; +/// +/// std::cout << color; // Write a color +/// std::cin >> color; // Read a color +/// +/// The current implementation is restricted to enumeration types whose values +/// can all be represented in a regular integer. +template class Enum { +public: + using base_enum_type = E; + + Enum(E = {}) noexcept; + + operator E() const noexcept; + + const std::string& str() const; + + bool str(const std::string*&) const noexcept; + + /// \return True if, and only if successful. + static bool parse(const std::string& string, E& value); + +private: + E m_value = E{}; +}; + +template +std::basic_ostream& operator<<(std::basic_ostream&, + const Enum&); + +template +std::basic_istream& operator>>(std::basic_istream&, + Enum&); + + +struct EnumAssoc { + const int value; + const char* const name; +}; + + + + +// Implementation + +} // namespace util + +namespace _impl { + +class EnumMapper { +public: + EnumMapper(const util::EnumAssoc*, bool ignore_case); + + bool parse(const std::string& string, int& value, bool ignore_case) const; + + std::map value_to_name; + std::map name_to_value; +}; + +template const EnumMapper& get_enum_mapper() +{ + static EnumMapper mapper{S::map, ignore_case}; // Throws + return mapper; +} + +} // namespace _impl + +namespace util { + +template +inline Enum::Enum(E value) noexcept : + m_value{value} +{ +} + +template +inline Enum::operator E() const noexcept +{ + return m_value; +} + +template +inline const std::string& Enum::str() const +{ + return _impl::get_enum_mapper().val_to_name.at(m_value); // Throws +} + +template +inline bool Enum::str(const std::string*& string) const noexcept +{ + const auto& value_to_name = _impl::get_enum_mapper().value_to_name; + auto i = value_to_name.find(int(m_value)); + if (i == value_to_name.end()) + return false; + string = &i->second; + return true; +} + +template +inline bool Enum::parse(const std::string& string, E& value) +{ + int value_2; + if (!_impl::get_enum_mapper().parse(string, value_2, ignore_case)) // Throws + return false; + value = E(value_2); + return true; +} + +template +inline std::basic_ostream& operator<<(std::basic_ostream& out, + const Enum& e) +{ + const std::string* string; + if (e.str(string)) { + out << *string; + } + else { + out << int(E(e)); + } + return out; +} + +template +std::basic_istream& operator>>(std::basic_istream& in, + Enum& e) +{ + if (in.bad() || in.fail()) + return in; + std::string string; + const std::ctype& ctype = std::use_facet>(in.getloc()); + C underscore(ctype.widen('_')); + for (;;) { + C ch; + // Allow white-spaces to be skipped when stream is configured + // that way + if (string.empty()) { + in >> ch; + } + else { + in.get(ch); + } + if (!in) { + if (in.bad()) + return in; + in.clear(in.rdstate() & ~std::ios_base::failbit); + break; + } + if (!ctype.is(std::ctype_base::alnum, ch) && ch != underscore) { + in.unget(); + break; + } + char ch_2 = ctype.narrow(ch, '\0'); + string += ch_2; + } + E value = E{}; + if (!Enum::parse(string, value)) { // Throws + in.setstate(std::ios_base::badbit); + } + else { + e = value; + } + return in; +} + +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_ENUM_HPP diff --git a/src/vendor-include/realm-ios/include/realm/util/errno.hpp b/src/vendor-include/realm-ios/include/realm/util/errno.hpp new file mode 100644 index 000000000..4907f369d --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/errno.hpp @@ -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. + * + **************************************************************************/ + +#ifndef REALM_UTIL_ERRNO_HPP +#define REALM_UTIL_ERRNO_HPP + +#include + +#include + + +namespace realm { +namespace util { + +// Get the error message for a given error code, and append it to `prefix` +inline std::string get_errno_msg(const char* prefix, int err) +{ + return prefix + make_basic_system_error_code(err).message(); +} + +} // namespace util +} // namespace realm + +#endif diff --git a/src/vendor-include/realm-ios/include/realm/util/features.h b/src/vendor-include/realm-ios/include/realm/util/features.h new file mode 100644 index 000000000..7cb54e392 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/features.h @@ -0,0 +1,344 @@ +/************************************************************************* + * + * 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 + +#if defined(_WIN32) && !defined(NOMINMAX) +#define NOMINMAX +#endif + +#ifndef REALM_NO_CONFIG +#include +#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 + +#ifdef __has_cpp_attribute +#define REALM_HAS_CPP_ATTRIBUTE(attr) __has_cpp_attribute(attr) +#else +#define REALM_HAS_CPP_ATTRIBUTE(attr) 0 +#endif + +#if REALM_HAS_CPP_ATTRIBUTE(clang::fallthrough) +#define REALM_FALLTHROUGH [[clang::fallthrough]] +#elif REALM_HAS_CPP_ATTRIBUTE(gnu::fallthrough) +#define REALM_FALLTHROUGH [[gnu::fallthrough]] +#elif REALM_HAS_CPP_ATTRIBUTE(fallthrough) +#define REALM_FALLTHROUGH [[fallthrough]] +#else +#define REALM_FALLTHROUGH +#endif + +// This should be renamed to REALM_UNREACHABLE as soon as REALM_UNREACHABLE is renamed to +// REALM_ASSERT_NOT_REACHED which will better reflect its nature +#if defined(__GNUC__) || defined(__clang__) +#define REALM_COMPILER_HINT_UNREACHABLE __builtin_unreachable +#else +#define REALM_COMPILER_HINT_UNREACHABLE abort +#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 + + +// FIXME: Change this to use [[nodiscard]] in C++17. +#if defined(__GNUC__) || defined(__HP_aCC) +#define REALM_NODISCARD __attribute__((warn_unused_result)) +#elif defined(_MSC_VER) +#define REALM_NODISCARD _Check_return_ +#else +#define REALM_NODISCARD +#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 || defined __ANDROID_API__ +#define REALM_ANDROID 1 +#else +#define REALM_ANDROID 0 +#endif + +#if defined _WIN32 +#include +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM) +#define REALM_WINDOWS 1 +#define REALM_UWP 0 +#elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) +#define REALM_WINDOWS 0 +#define REALM_UWP 1 +#endif +#else +#define REALM_WINDOWS 0 +#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 +#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 +#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 + +// asl_log is deprecated in favor of os_log as of the following versions: +// macos(10.12), ios(10.0), watchos(3.0), tvos(10.0) +// versions are defined in /usr/include/Availability.h +// __MAC_10_12 101200 +// __IPHONE_10_0 100000 +// __WATCHOS_3_0 30000 +// __TVOS_10_0 100000 +#if REALM_PLATFORM_APPLE \ + && ( \ + (REALM_IOS && defined(__IPHONE_OS_VERSION_MIN_REQUIRED) \ + && __IPHONE_OS_VERSION_MIN_REQUIRED >= 100000) \ + || (REALM_TVOS && defined(__TV_OS_VERSION_MIN_REQUIRED) \ + && __TV_OS_VERSION_MIN_REQUIRED >= 100000) \ + || (REALM_WATCHOS && defined(__WATCH_OS_VERSION_MIN_REQUIRED) \ + && __WATCH_OS_VERSION_MIN_REQUIRED >= 30000) \ + || (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) \ + && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200) \ + ) +#define REALM_APPLE_OS_LOG 1 +#else +#define REALM_APPLE_OS_LOG 0 +#endif + +#if REALM_ANDROID || REALM_IOS || REALM_WATCHOS || REALM_TVOS || REALM_UWP +#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 FIXME Async commits not supported +#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 + +// Address Sanitizer +#if defined(__has_feature) // Clang +# if __has_feature(address_sanitizer) +# define REALM_SANITIZE_ADDRESS 1 +# else +# define REALM_SANITIZE_ADDRESS 0 +# endif +#elif defined(__SANITIZE_ADDRESS__) && __SANITIZE_ADDRESS__ // GCC +# define REALM_SANITIZE_ADDRESS 1 +#else +# define REALM_SANITIZE_ADDRESS 0 +#endif + +// Thread Sanitizer +#if defined(__has_feature) // Clang +# if __has_feature(thread_sanitizer) +# define REALM_SANITIZE_THREAD 1 +# else +# define REALM_SANITIZE_THREAD 0 +# endif +#elif defined(__SANITIZE_THREAD__) && __SANITIZE_THREAD__ // GCC +# define REALM_SANITIZE_THREAD 1 +#else +# define REALM_SANITIZE_THREAD 0 +#endif + +#endif /* REALM_UTIL_FEATURES_H */ diff --git a/src/vendor-include/realm-ios/include/realm/util/fifo_helper.hpp b/src/vendor-include/realm-ios/include/realm/util/fifo_helper.hpp new file mode 100644 index 000000000..6ca8d75b0 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/fifo_helper.hpp @@ -0,0 +1,43 @@ +/************************************************************************* + * + * Copyright 2019 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_FIFO_HELPER_HPP +#define REALM_UTIL_FIFO_HELPER_HPP + +#include + +namespace realm { +namespace util { + +// Attempts to create a FIFO file at the location determined by `path`. +// If creating the FIFO at this location fails, an exception is thrown. +// If a FIFO already exists at the given location, this method does nothing. +void create_fifo(std::string path); // throws + +// Same as above, but returns `false` if the FIFO could not be created instead of throwing. +bool try_create_fifo(const std::string& path); + +// Ensure that a path representing a directory ends with `/` +inline std::string normalize_dir(const std::string& path) { + return (!path.empty() && path.back() != '/') ? path + '/' : path; +} + +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_FIFO_HELPER_HPP diff --git a/src/vendor-include/realm-ios/include/realm/util/file.hpp b/src/vendor-include/realm-ios/include/realm/util/file.hpp new file mode 100644 index 000000000..b8f78bce1 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/file.hpp @@ -0,0 +1,1322 @@ +/************************************************************************* + * + * 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 +#include + +#ifndef _WIN32 +#include // POSIX.1-2001 +#endif + +#if defined(_MSC_VER) && _MSC_VER >= 1900 // compiling with at least Visual Studio 2015 +#if _MSVC_LANG >= 201703L +#include +#else +#define _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING // switch to once we switch to C++17 +#include +namespace std { + namespace filesystem = std::experimental::filesystem::v1; +} +#endif +#define REALM_HAVE_STD_FILESYSTEM 1 +#else +#define REALM_HAVE_STD_FILESYSTEM 0 +#endif + +#include +#include +#include +#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); + +/// Same as remove_dir() except that this one returns false, rather +/// than throwing an exception, if the specified directory did not +/// exist. If the directory did exist, and was deleted, this function +/// returns true. +bool try_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. +/// +/// remove_dir_recursive() assumes that no other process or thread is making +/// simultaneous changes in the directory. +void remove_dir_recursive(const std::string& path); + +/// Same as remove_dir_recursive() except that this one returns false, rather +/// than throwing an exception, if the specified directory did not +/// exist. If the directory did exist, and was deleted, this function +/// returns true. +/// +/// try_remove_dir_recursive() assumes that no other process or thread is making +/// simultaneous changes in the directory. +bool try_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); + static size_t read_static(FileDesc fd, 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); + static void write_static(FileDesc fd, const char* data, size_t size); + + // Tells current file pointer of fd + static uint64_t get_file_pos(FileDesc fd); + + /// 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; + static SizeType get_size_static(FileDesc fd); + + /// 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); + + /// Same effect as prealloc_if_supported(original_size, new_size); + /// + /// 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(size_t new_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() + bool 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); + static void seek_static(FileDesc, 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); + + /// Get the encryption key set by set_encryption_key(), + /// null_ptr if no key set. + const char* get_encryption_key() const; + 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; + void* map_fixed(AccessMode, void* address, size_t size, int map_flags = 0, size_t offset = 0) const; + void* map_reserve(AccessMode, size_t size, size_t offset) 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; + void* map_fixed(AccessMode, void* address, size_t size, EncryptedFileMapping* mapping, int map_flags = 0, + size_t offset = 0) const; + void* map_reserve(AccessMode, size_t size, size_t offset, EncryptedFileMapping*& mapping) 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(FileDesc fd, 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 throwing 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; + static bool is_same_file_static(FileDesc f1, FileDesc f2); + + // 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 = util::FunctionRef; + + /// 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 + UniqueID() + : device(0) + , inode(0) + { + } + UniqueID(dev_t d, ino_t i) + : device(d) + , inode(i) + { + } + // 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. + dev_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 the file descriptor for the file + FileDesc get_descriptor() const; + // Return the path of the open file, or an empty string if + // this file has never been opened. + std::string get_path() 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_fd; + bool m_have_lock; // Only valid when m_fd is not null +#else + int m_fd; +#endif + std::unique_ptr m_encryption_key = nullptr; + std::string m_path; + + 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; + mutable size_t m_size = 0; + size_t m_offset = 0; + FileDesc m_fd; + + 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; + + // Use + 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 + mutable 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) noexcept + { + if (m_addr) + unmap(); + m_addr = other.get_addr(); + m_size = other.m_size; + m_offset = other.m_offset; + m_fd = other.m_fd; + other.m_offset = 0; + other.m_addr = nullptr; + other.m_size = 0; +#if REALM_ENABLE_ENCRYPTION + m_encrypted_mapping = other.m_encrypted_mapping; + other.m_encrypted_mapping = nullptr; +#endif + return *this; + } + Map(Map&& other) noexcept + { + *this = std::move(other); + } + + /// 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*, size_t = 4096); + ~Streambuf() noexcept; + + // Disable copying + Streambuf(const Streambuf&) = delete; + Streambuf& operator=(const Streambuf&) = delete; + +private: + 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 ExceptionWithBacktrace { +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; + + const char* message() const noexcept + { + m_buffer = std::runtime_error::what(); + if (m_path.size() > 0) + m_buffer += (std::string(" Path: ") + m_path); + return m_buffer.c_str(); + } + +private: + std::string m_path; + mutable std::string m_buffer; +}; + + +/// 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; +#elif REALM_HAVE_STD_FILESYSTEM + std::filesystem::directory_iterator m_iterator; +#endif +}; + + +// Implementation: + +inline File::File(const std::string& path, Mode m) +{ +#ifdef _WIN32 + m_fd = nullptr; +#else + m_fd = -1; +#endif + + open(path, m); +} + +inline File::File() noexcept +{ +#ifdef _WIN32 + m_fd = nullptr; +#else + m_fd = -1; +#endif +} + +inline File::~File() noexcept +{ + close(); +} + +inline File::File(File&& f) noexcept +{ +#ifdef _WIN32 + m_fd = f.m_fd; + m_have_lock = f.m_have_lock; + f.m_fd = 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_fd = f.m_fd; + m_have_lock = f.m_have_lock; + f.m_fd = 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_fd != 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; + m_size = 0; +} + +inline File::MapBase::~MapBase() noexcept +{ + unmap(); +} + + +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); + // missing sync() here? + unmap(); + map(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; + m_fd = 0; + return addr; +} + + +inline File::Streambuf::Streambuf(File* f, size_t buffer_size) + : 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 util::overflow_error("Seek position overflow"); + m_file.seek(pos2); + return pos; +} + +inline void File::Streambuf::flush() +{ + size_t n = pptr() - pbase(); + if (n > 0) { + m_file.write(pbase(), n); + setp(m_buffer.get(), epptr()); + } +} + +inline File::AccessError::AccessError(const std::string& msg, const std::string& path) + : ExceptionWithBacktrace(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 util::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 util::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/src/vendor-include/realm-ios/include/realm/util/file_is_regular.hpp b/src/vendor-include/realm-ios/include/realm/util/file_is_regular.hpp new file mode 100644 index 000000000..e9454d298 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/file_is_regular.hpp @@ -0,0 +1,37 @@ +/************************************************************************* + * + * 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_FILE_IS_REGULAR_HPP +#define REALM_UTIL_FILE_IS_REGULAR_HPP + +#include + +namespace realm { +namespace util { + +/// Check whether the specified file is a reguler file. +/// +/// FIXME: This function ought to be moved to in the +/// realm-core repository. +bool file_is_regular(const std::string& path); + +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_FILE_IS_REGULAR_HPP diff --git a/src/vendor-include/realm-ios/include/realm/util/file_mapper.hpp b/src/vendor-include/realm-ios/include/realm/util/file_mapper.hpp new file mode 100644 index 000000000..50f00135c --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/file_mapper.hpp @@ -0,0 +1,188 @@ +/************************************************************************* + * + * 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 +#include + +#include + +namespace realm { +namespace util { + +void* mmap(FileDesc fd, size_t size, File::AccessMode access, size_t offset, const char* encryption_key); +void* mmap_fixed(FileDesc fd, void* address_request, size_t size, File::AccessMode access, size_t offset, + const char* enc_key); +void* mmap_reserve(FileDesc fd, size_t size, size_t offset); +void munmap(void* addr, size_t size); +void* mremap(FileDesc 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(FileDesc fd, void* addr, size_t size); +void* mmap_anon(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; + +class PageReclaimGovernor { +public: + // Called by the page reclaimer with the current load (in bytes) and + // must return the target load (also in bytes). Returns no_match if no + // target can be set + static constexpr int64_t no_match = -1; + virtual std::function current_target_getter(size_t load) = 0; + virtual void report_target_result(int64_t) = 0; +}; + +// Set a page reclaim governor. The governor is an object with a method which will be called periodically +// and must return a 'target' amount of memory to hold decrypted pages. The page reclaim daemon +// will then try to release pages to meet the target. The governor is called with the current +// amount of data used, for the purpose of logging - or possibly for computing the target +// +// The governor is called approximately once per second. +// +// If no governor is installed, the page reclaim daemon will not start. +void set_page_reclaim_governor(PageReclaimGovernor* governor); + +// Use the default governor. The default governor is used automatically if nothing else is set, so +// this funciton is mostly useful for tests where changing back to the default could be desirable. +inline void set_page_reclaim_governor_to_default() +{ + set_page_reclaim_governor(nullptr); +} + +// Retrieves the number of in memory decrypted pages, across all open files. +size_t get_num_decrypted_pages(); + +// Retrieves the +// - amount of memory used for decrypted pages, across all open files. +// - current target for the reclaimer (desired number of decrypted pages) +// - current workload size for the reclaimer, across all open files. +struct decrypted_memory_stats_t { + size_t memory_size; + size_t reclaimer_target; + size_t reclaimer_workload; +}; + +decrypted_memory_stats_t get_decrypted_memory_stats(); + +#if REALM_ENABLE_ENCRYPTION + +void encryption_note_reader_start(SharedFileInfo& info, const void* reader_id); +void encryption_note_reader_end(SharedFileInfo& info, const void* reader_id) noexcept; + +SharedFileInfo* get_file_info_for_file(File& file); + +// This variant allows the caller to obtain direct access to the encrypted file mapping +// for optimization purposes. +void* mmap(FileDesc fd, size_t size, File::AccessMode access, size_t offset, const char* encryption_key, + EncryptedFileMapping*& mapping); +void* mmap_fixed(FileDesc fd, void* address_request, size_t size, File::AccessMode access, size_t offset, + const char* enc_key, EncryptedFileMapping* mapping); + +void* mmap_reserve(FileDesc fd, size_t size, File::AccessMode am, size_t offset, const char* enc_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); + mapping->read_barrier(addr, size, 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 set_page_reclaim_governor(PageReclaimGovernor*) +{ +} + +size_t inline get_num_decrypted_pages() +{ + return 0; +} + +void inline encryption_read_barrier(const void*, size_t, EncryptedFileMapping*, HeaderToSize = nullptr) +{ +} + +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(const 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(const 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/src/vendor-include/realm-ios/include/realm/util/fixed_size_buffer.hpp b/src/vendor-include/realm-ios/include/realm/util/fixed_size_buffer.hpp new file mode 100644 index 000000000..8a512ee46 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/fixed_size_buffer.hpp @@ -0,0 +1,135 @@ +/************************************************************************* + * + * Copyright 2019 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_FIXED_SIZE_BUFFER_HPP +#define REALM_UTIL_FIXED_SIZE_BUFFER_HPP + +#include +#include +#include + +namespace realm { +namespace util { + +/// This is a buffer with a fixed size. You can only insert elements. +/// When the number of elements inserted matches the size of the buffer, +/// additional insertions will overwrite the oldest elements. +template +class FixedSizeBuffer { +public: + class iterator; + + FixedSizeBuffer(size_t sz) + : m_size(sz) + { + if (sz == 0) + throw std::runtime_error("FixedSizeBuffer size cannot be 0"); + m_buffer.reserve(sz); + } + size_t size() + { + return m_buffer.size(); + } + void insert(const T& val) + { + if (m_buffer.size() < m_size) { + m_buffer.emplace_back(val); + } + else { + m_buffer[m_oldest] = val; + ++m_oldest; + if (m_oldest == m_size) + m_oldest = 0; + } + } + T& at(size_t n) + { + auto idx = (n + m_oldest) % m_size; + return m_buffer[idx]; + } + T& operator[](size_t n) + { + return at(n); + } + iterator begin() + { + return iterator(*this, 0); + } + iterator end() + { + return iterator(*this, m_buffer.size()); + } + +private: + std::vector m_buffer; + size_t m_size; + size_t m_oldest = 0; +}; + +template +class FixedSizeBuffer::iterator { +public: + typedef std::forward_iterator_tag iterator_category; + typedef T value_type; + typedef ptrdiff_t difference_type; + typedef T* pointer; + typedef T& reference; + + iterator(FixedSizeBuffer& b, size_t ndx) + : m_cb(b) + , m_ndx(ndx) + { + } + pointer operator->() + { + return &m_cb[m_ndx]; + } + reference operator*() + { + return m_cb[m_ndx]; + } + iterator& operator++() + { + ++m_ndx; + return *this; + } + iterator operator++(int) + { + iterator tmp(*this); + operator++(); + return tmp; + } + bool operator!=(const iterator& rhs) + { + return m_ndx != rhs.m_ndx; + } + bool operator==(const iterator& rhs) + { + return m_ndx == rhs.m_ndx; + } + +private: + FixedSizeBuffer& m_cb; + size_t m_ndx; +}; + +} // namespace util +} // namespace realm + + +#endif /* REALM_UTIL_FIXED_SIZE_BUFFER_HPP */ diff --git a/src/vendor-include/realm-ios/include/realm/util/flat_map.hpp b/src/vendor-include/realm-ios/include/realm/util/flat_map.hpp new file mode 100644 index 000000000..a3d6b540d --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/flat_map.hpp @@ -0,0 +1,201 @@ + +/************************************************************************* + * + * REALM CONFIDENTIAL + * __________________ + * + * [2011] - [2017] 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_FLAT_MAP_HPP +#define REALM_UTIL_FLAT_MAP_HPP + +#include +#include // std::pair +#include // std::lower_bound etc. +#include + +#include + +namespace realm { +namespace util { + +template >, class Cmp = std::less<>> +struct FlatMap { + using value_type = std::pair; + using key_type = K; + using mapped_type = V; + FlatMap() {} + FlatMap(const FlatMap&) = default; + FlatMap(FlatMap&&) = default; + FlatMap& operator=(const FlatMap&) = default; + FlatMap& operator=(FlatMap&&) = default; + + V& at(K key) + { + auto it = lower_bound(key); + if (it == end() || it->first != key) + it = m_data.emplace(it, std::move(key), V{}); // Throws + return it->second; + } + + const V& at(const K& key) const + { + auto it = find(key); + if (it == end()) + throw util::out_of_range("no such key"); + return it->second; + } + + V& operator[](const K& key) + { + return at(key); // Throws + } + + using iterator = typename Container::iterator; + using const_iterator = typename Container::const_iterator; + iterator begin() noexcept { return m_data.begin(); } + iterator end() noexcept { return m_data.end(); } + const_iterator begin() const noexcept { return m_data.begin(); } + const_iterator end() const noexcept { return m_data.end(); } + + + bool empty() const noexcept { return m_data.empty(); } + size_t size() const noexcept { return m_data.size(); } + void clear() noexcept { m_data.clear(); } + + std::pair insert(value_type value) + { + auto it = lower_bound(value.first); + if (it != end() && it->first == value.first) { + return std::make_pair(it, false); + } + return std::make_pair(m_data.emplace(it, std::move(value)), true); // Throws + } + + template + std::pair insert(P pair) + { + return insert(value_type{std::get<0>(pair), std::get<1>(pair)}); + } + + template + void insert(InputIt first, InputIt last) + { + for (auto it = first; it != last; ++it) { + insert(*it); + } + } + + template + std::pair emplace(Args&&... args) + { + value_type value{std::forward(args)...}; + return insert(std::move(value)); + } + + template + std::pair emplace_hint(const_iterator pos, Args&&... args) + { + static_cast(pos); // FIXME: TODO + return emplace(std::forward(args)...); + } + + iterator erase(const_iterator pos) noexcept(std::is_nothrow_move_assignable::value) + { + return m_data.erase(pos); + } + + iterator erase(const_iterator first, const_iterator last) noexcept(std::is_nothrow_move_assignable::value) + { + return m_data.erase(first, last); + } + + size_t erase(const K& key) noexcept(std::is_nothrow_move_assignable::value) + { + auto it = find(key); + if (it != end()) { + erase(it); + return 1; + } + return 0; + } + + void swap(FlatMap& other) + { + m_data.swap(other.m_data); + } + + template + size_t count(const Key& key) const noexcept + { + return find(key) == end() ? 0 : 1; + } + + template + iterator find(const Key& key) noexcept + { + const FlatMap* This = this; + const_iterator pos = This->find(key); + return iterator{begin() + (pos - This->begin())}; + } + + template + const_iterator find(const Key& key) const noexcept + { + auto it = lower_bound(key); + if (it != end() && it->first != key) { + return end(); + } + return it; + } + + template + iterator lower_bound(const Key& key) noexcept + { + const FlatMap* This = this; + const_iterator pos = This->lower_bound(key); + return iterator{begin() + (pos - This->begin())}; + } + + template + const_iterator lower_bound(const Key& key) const noexcept + { + auto it = std::lower_bound(begin(), end(), key, [](const value_type& a, const Key& b) { + return Cmp{}(a.first, b); + }); + return it; + } + + // FIXME: Not implemented yet. + template + iterator upper_bound(const Key&) noexcept; + // FIXME: Not implemented yet. + template + const_iterator upper_bound(const Key&) const noexcept; + + void reserve(size_t size) + { + m_data.reserve(size); // Throws + } + +private: + Container m_data; +}; + +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_FLAT_MAP_HPP diff --git a/src/vendor-include/realm-ios/include/realm/util/function_ref.hpp b/src/vendor-include/realm-ios/include/realm/util/function_ref.hpp new file mode 100644 index 000000000..322a573fc --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/function_ref.hpp @@ -0,0 +1,94 @@ +/************************************************************************* + * + * Copyright 2019 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_FUNCTION_REF_HPP +#define REALM_UTIL_FUNCTION_REF_HPP + +#include +#include + +namespace realm { +namespace util { + +/// A lightweight non-owning reference to a callable. +/// +/// This type is similar to std::function, but unlike std::function holds a reference to the callable rather than +/// making a copy of it. This means that it will never require a heap allocation, and produces significantly smaller +/// binaries due to the template machinery being much simpler. This type should only ever be used as a function +/// parameter that is not stored past when the function returns. All other uses (including trying to store it in a +/// std::function) are very unlikely to be correct. +/// +/// This implements a subset of P0792R5, which hopefully will be incorportated into a future version of the standard +/// library. +template +class FunctionRef; +template +class FunctionRef { +public: + // A FunctionRef is never empty, and so cannot be default-constructed. + constexpr FunctionRef() noexcept = delete; + + // FunctionRef is copyable and moveable. +#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && !defined(__clang__) + FunctionRef(FunctionRef const&) noexcept = default; + FunctionRef& operator=(const FunctionRef&) noexcept = default; + FunctionRef(FunctionRef&&) noexcept = default; + FunctionRef& operator=(FunctionRef&&) noexcept = default; +#else + constexpr FunctionRef(FunctionRef const&) noexcept = default; + constexpr FunctionRef& operator=(const FunctionRef&) noexcept = default; + constexpr FunctionRef(FunctionRef&&) noexcept = default; + constexpr FunctionRef& operator=(FunctionRef&&) noexcept = default; +#endif + + // Construct a FunctionRef which wraps the given callable. + template + constexpr FunctionRef(F&& f) noexcept + : m_obj(const_cast(reinterpret_cast(std::addressof(f)))) + , m_callback([](void* obj, Args... args) -> Return { + return (*reinterpret_cast::type>(obj))(std::forward(args)...); + }) + { + } + + constexpr void swap(FunctionRef& rhs) noexcept + { + std::swap(m_obj, rhs.m_obj); + std::swap(m_callback, rhs.m_callback); + } + + Return operator()(Args... args) const + { + return m_callback(m_obj, std::forward(args)...); + } + +private: + void* m_obj; + Return (*m_callback)(void*, Args...); +}; + +template +constexpr void swap(FunctionRef& lhs, FunctionRef& rhs) noexcept +{ + lhs.swap(rhs); +} + +} // namespace util +} // namespace realm + +#endif diff --git a/src/vendor-include/realm-ios/include/realm/util/get_file_size.hpp b/src/vendor-include/realm-ios/include/realm/util/get_file_size.hpp new file mode 100644 index 000000000..29ca1a7b0 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/get_file_size.hpp @@ -0,0 +1,45 @@ +/************************************************************************* + * + * 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_GET_FILE_SIZE_HPP +#define REALM_UTIL_GET_FILE_SIZE_HPP + +#include + +namespace realm { +namespace util { + +/// FIXME: This function ought to be moved to in the +/// realm-core repository. +util::File::SizeType get_file_size(const std::string& path); + + + +// Implementation + +inline util::File::SizeType get_file_size(const std::string& path) +{ + util::File file{path}; // Throws + return file.get_size(); // Throws +} + +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_GET_FILE_SIZE_HPP diff --git a/src/vendor-include/realm-ios/include/realm/util/hex_dump.hpp b/src/vendor-include/realm-ios/include/realm/util/hex_dump.hpp new file mode 100644 index 000000000..08f5d2b34 --- /dev/null +++ b/src/vendor-include/realm-ios/include/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/src/vendor-include/realm-ios/include/realm/util/http.hpp b/src/vendor-include/realm-ios/include/realm/util/http.hpp new file mode 100644 index 000000000..91da87630 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/http.hpp @@ -0,0 +1,541 @@ +/************************************************************************* + * + * 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 + +#include +#include +#include +#include +#include + +namespace realm { +namespace util { +enum class HTTPParserError { + None = 0, + ContentTooLong, + HeaderLineTooLong, + MalformedResponse, + MalformedRequest, + BadRequest, +}; +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, +}; + +bool valid_http_status_code(unsigned int code); + +/// See: https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html +enum class HTTPMethod { + Options, + Get, + Head, + Post, + Put, + Delete, + Trace, + Connect, +}; + +struct HTTPAuthorization { + std::string scheme; + std::map values; +}; + +HTTPAuthorization parse_authorization(const std::string&); + +class HeterogeneousCaseInsensitiveCompare { +public: + using is_transparent = std::true_type; + template bool operator()(const A& a, const B& b) const noexcept + { + return comp(StringView(a), StringView(b)); + } +private: + bool comp(StringView a, StringView b) const noexcept + { + 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; + std::string reason; + 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 { + util::Logger& logger; + + // FIXME: Generally useful? + struct CallocDeleter { + void operator()(void* ptr) + { + std::free(ptr); + } + }; + + HTTPParserBase(util::Logger& logger_2): + logger {logger_2} + { + // 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, util::Logger& logger); + + void set_write_buffer(const HTTPRequest&); + void set_write_buffer(const HTTPResponse&); +}; + + +template +struct HTTPParser: protected HTTPParserBase { + explicit HTTPParser(Socket& socket, util::Logger& logger): + HTTPParserBase(logger), + 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; + } + if (!parse_header_line(n)) { + on_complete(HTTPParserError::BadRequest); + return; + } + + // 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, util::Logger& logger) : HTTPParser(socket, logger) {} + + /// 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 util::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, this->logger)) { + m_response.status = status; + m_response.reason = reason; + 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); + m_handler = nullptr; + 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, util::Logger& logger): HTTPParser(socket, logger) + {} + + /// 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 util::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 util::runtime_error("No request in progress."); + } + if (m_respond_handler) { + // FIXME: Proper exception type. + throw util::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); + } +}; + + +std::string make_http_host(bool is_ssl, StringView address, std::uint_fast16_t port); + +} // namespace util +} // namespace realm + + +#endif // REALM_UTIL_HTTP_HPP diff --git a/src/vendor-include/realm-ios/include/realm/util/inspect.hpp b/src/vendor-include/realm-ios/include/realm/util/inspect.hpp new file mode 100644 index 000000000..84a669db6 --- /dev/null +++ b/src/vendor-include/realm-ios/include/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/src/vendor-include/realm-ios/include/realm/util/interprocess_condvar.hpp b/src/vendor-include/realm-ios/include/realm/util/interprocess_condvar.hpp new file mode 100644 index 000000000..0e5b5866b --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/interprocess_condvar.hpp @@ -0,0 +1,151 @@ +/************************************************************************* + * + * 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 + +// Condvar Emulation is required if RobustMutex emulation is enabled +#if defined(REALM_ROBUST_MUTEX_EMULATION) || defined(_WIN32) +#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 { +#ifdef _WIN32 + // Number of waiting threads. + int32_t m_waiters_count; + size_t m_was_broadcast; +#else + uint64_t signal_counter; + uint64_t wait_counter; +#endif + }; +#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; + +#ifdef _WIN32 + // Semaphore used to queue up threads waiting for the condition to + // become signaled. + HANDLE m_sema = 0; + // An auto-reset event used by the broadcast/signal thread to wait + // for all the waiting thread(s) to wake up and be released from the + // semaphore. + HANDLE m_waiters_done = 0; + std::string m_name; + + // Serialize access to m_waiters_count + InterprocessMutex m_waiters_lockcount; +#endif + +#endif +}; + + +// Implementation: + + +} // namespace util +} // namespace realm + + +#endif diff --git a/src/vendor-include/realm-ios/include/realm/util/interprocess_mutex.hpp b/src/vendor-include/realm-ios/include/realm/util/interprocess_mutex.hpp new file mode 100644 index 000000000..b3af1c6cd --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/interprocess_mutex.hpp @@ -0,0 +1,375 @@ +/************************************************************************* + * + * 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 +#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; + +#if defined(REALM_ROBUST_MUTEX_EMULATION) || defined(_WIN32) + 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; + +#ifdef _WIN32 + HANDLE m_handle = 0; +#endif + +#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 _WIN32 + if (m_handle) { + bool b = CloseHandle(m_handle); + REALM_ASSERT_RELEASE(b); + } +#endif + +#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; +#elif defined(_WIN32) + if (m_handle) { + bool b = CloseHandle(m_handle); + REALM_ASSERT_RELEASE(b); + } + // replace backslashes because they're significant in object namespace names + std::string path_escaped = path; + std::replace(path_escaped.begin(), path_escaped.end(), '\\', '/'); + std::string name = "Local\\realm_named_intermutex_" + path_escaped + mutex_name; + + std::wstring wname(name.begin(), name.end()); + m_handle = CreateMutexW(0, false, wname.c_str()); + if (!m_handle) { + throw std::system_error(GetLastError(), std::system_category(), "Error opening mutex"); + } +#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 + +#ifdef _WIN32 + DWORD d = WaitForSingleObject(m_handle, INFINITE); + REALM_ASSERT_RELEASE(d != WAIT_FAILED); +#else + REALM_ASSERT(m_shared_part); + m_shared_part->lock([]() {}); +#endif +#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 + +#ifdef _WIN32 + DWORD ret = WaitForSingleObject(m_handle, 0); + REALM_ASSERT_RELEASE(ret != WAIT_FAILED); + + if (ret == WAIT_OBJECT_0) { + return true; + } + else { + return false; + } +#else + REALM_ASSERT(m_shared_part); + return m_shared_part->try_lock([]() {}); +#endif +#endif +} + + +inline void InterprocessMutex::unlock() +{ +#ifdef REALM_ROBUST_MUTEX_EMULATION + m_lock_info->m_file.unlock(); + m_lock_info->m_local_mutex.unlock(); +#else +#ifdef _WIN32 + bool b = ReleaseMutex(m_handle); + REALM_ASSERT_RELEASE(b); +#else + REALM_ASSERT(m_shared_part); + m_shared_part->unlock(); +#endif +#endif +} + + +inline bool InterprocessMutex::is_valid() noexcept +{ +#ifdef REALM_ROBUST_MUTEX_EMULATION + return true; +#elif defined(_WIN32) + // There is no safe way of testing if the m_handle mutex handle is valid on Windows, without having bad side effects + // for the cases where it is indeed invalid. If m_handle contains an arbitrary value, it might by coincidence be equal + // to a real live handle of another kind. This excludes a try_lock implementation and many other ideas. + 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/src/vendor-include/realm-ios/include/realm/util/json_parser.hpp b/src/vendor-include/realm-ios/include/realm/util/json_parser.hpp new file mode 100644 index 000000000..bc6664ae4 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/json_parser.hpp @@ -0,0 +1,545 @@ +/************************************************************************* + * + * 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 + +#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/src/vendor-include/realm-ios/include/realm/util/load_file.hpp b/src/vendor-include/realm-ios/include/realm/util/load_file.hpp new file mode 100644 index 000000000..14090fcfa --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/load_file.hpp @@ -0,0 +1,36 @@ +/************************************************************************* + * + * 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_LOAD_FILE_HPP +#define REALM_UTIL_LOAD_FILE_HPP + +#include + +namespace realm { +namespace util { + +// FIXME: These functions ought to be moved to in the +// realm-core repository. +std::string load_file(const std::string& path); +std::string load_file_and_chomp(const std::string& path); + +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_LOAD_FILE_HPP diff --git a/src/vendor-include/realm-ios/include/realm/util/logger.hpp b/src/vendor-include/realm-ios/include/realm/util/logger.hpp new file mode 100644 index 000000000..0946208f3 --- /dev/null +++ b/src/vendor-include/realm-ios/include/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/src/vendor-include/realm-ios/include/realm/util/memory_stream.hpp b/src/vendor-include/realm-ios/include/realm/util/memory_stream.hpp new file mode 100644 index 000000000..81a48523c --- /dev/null +++ b/src/vendor-include/realm-ios/include/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* b, const char* e) noexcept +{ + m_begin = b; + m_end = e; + m_curr = b; +} + + +inline MemoryOutputStreambuf::MemoryOutputStreambuf() +{ +} + +inline MemoryOutputStreambuf::~MemoryOutputStreambuf() noexcept +{ +} + +inline void MemoryOutputStreambuf::set_buffer(char* b, char* e) noexcept +{ + setp(b, e); +} + +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* b, const char* e) noexcept +{ + m_streambuf.set_buffer(b, e); + clear(); +} + +template inline void MemoryInputStream::set_buffer(const char (&buffer)[N]) noexcept +{ + const char* b = buffer; + const char* e = b + N; + set_buffer(b, e); +} + +inline void MemoryInputStream::set_string(const std::string& str) noexcept +{ + const char* b = str.data(); + const char* e = b + str.size(); + set_buffer(b, e); +} + +inline void MemoryInputStream::set_c_string(const char* c_str) noexcept +{ + const char* b = c_str; + const char* e = b + traits_type::length(c_str); + set_buffer(b, e); +} + + +inline MemoryOutputStream::MemoryOutputStream() + : std::ostream(&m_streambuf) +{ +} + +inline MemoryOutputStream::~MemoryOutputStream() noexcept +{ +} + +inline void MemoryOutputStream::set_buffer(char* b, char* e) noexcept +{ + m_streambuf.set_buffer(b, e); + 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/src/vendor-include/realm-ios/include/realm/util/metered/deque.hpp b/src/vendor-include/realm-ios/include/realm/util/metered/deque.hpp new file mode 100644 index 000000000..41c1b7146 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/metered/deque.hpp @@ -0,0 +1,37 @@ +/************************************************************************* + * + * REALM CONFIDENTIAL + * __________________ + * + * [2011] - [2018] 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_METERED_DEQUE_HPP +#define REALM_UTIL_METERED_DEQUE_HPP + +#include +#include + +namespace realm { +namespace util { +namespace metered { +/// Vector with metered allocation +template > +using deque = std::deque; +} // namespace metered +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_METERED_DEQUE_HPP + diff --git a/src/vendor-include/realm-ios/include/realm/util/metered/map.hpp b/src/vendor-include/realm-ios/include/realm/util/metered/map.hpp new file mode 100644 index 000000000..be03f9ebd --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/metered/map.hpp @@ -0,0 +1,41 @@ +/************************************************************************* + * + * REALM CONFIDENTIAL + * __________________ + * + * [2011] - [2018] 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_METERED_MAP_HPP +#define REALM_UTIL_METERED_MAP_HPP + +#include +#include + +namespace realm { +namespace util { +namespace metered { +/// Map with metered allocation. Additionally, the default Compare is changed to +/// `std::less<>` instead of `std::less`, which allows heterogenous lookup. +template , + class Alloc = MeteredSTLAllocator>> +using map = std::map; +} // namespace metered +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_METERED_MAP_HPP + diff --git a/src/vendor-include/realm-ios/include/realm/util/metered/set.hpp b/src/vendor-include/realm-ios/include/realm/util/metered/set.hpp new file mode 100644 index 000000000..95f1e7348 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/metered/set.hpp @@ -0,0 +1,38 @@ +/************************************************************************* + * + * REALM CONFIDENTIAL + * __________________ + * + * [2011] - [2018] 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_METERED_SET_HPP +#define REALM_UTIL_METERED_SET_HPP + +#include +#include + +namespace realm { +namespace util { +namespace metered { +/// Set with metered allocation. Additionally, the default Compare is changed to +/// `std::less<>` instead of `std::less`, which allows heterogenous lookup. +template , class Alloc = MeteredSTLAllocator> +using set = std::set; +} // namespace metered +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_METERED_SET_HPP + diff --git a/src/vendor-include/realm-ios/include/realm/util/metered/string.hpp b/src/vendor-include/realm-ios/include/realm/util/metered/string.hpp new file mode 100644 index 000000000..b3a52db4d --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/metered/string.hpp @@ -0,0 +1,36 @@ +/************************************************************************* + * + * REALM CONFIDENTIAL + * __________________ + * + * [2011] - [2018] 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_METERED_STRING_HPP +#define REALM_UTIL_METERED_STRING_HPP + +#include +#include + +namespace realm { +namespace util { +namespace metered { +/// String with metered allocation +using string = std::basic_string, MeteredSTLAllocator>; +} // namespace metered +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_METERED_STRING_HPP + diff --git a/src/vendor-include/realm-ios/include/realm/util/metered/unordered_map.hpp b/src/vendor-include/realm-ios/include/realm/util/metered/unordered_map.hpp new file mode 100644 index 000000000..8ead21ade --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/metered/unordered_map.hpp @@ -0,0 +1,41 @@ +/************************************************************************* + * + * REALM CONFIDENTIAL + * __________________ + * + * [2011] - [2018] 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_METERED_UNORDERED_MAP_HPP +#define REALM_UTIL_METERED_UNORDERED_MAP_HPP + +#include +#include + +namespace realm { +namespace util { +namespace metered { +/// Unordered map with metered allocation +template , + class KeyEqual = std::equal_to, + class Alloc = MeteredSTLAllocator>> +using unordered_map = std::unordered_map; +} // namespace metered +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_METERED_UNORDERED_MAP_HPP + diff --git a/src/vendor-include/realm-ios/include/realm/util/metered/unordered_set.hpp b/src/vendor-include/realm-ios/include/realm/util/metered/unordered_set.hpp new file mode 100644 index 000000000..ad0dc5fae --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/metered/unordered_set.hpp @@ -0,0 +1,40 @@ +/************************************************************************* + * + * REALM CONFIDENTIAL + * __________________ + * + * [2011] - [2018] 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_METERED_UNORDERED_SET_HPP +#define REALM_UTIL_METERED_UNORDERED_SET_HPP + +#include +#include + +namespace realm { +namespace util { +namespace metered { +/// Unordered set with metered allocation +template , + class KeyEqual = std::equal_to, + class Alloc = MeteredSTLAllocator> +using unordered_set = std::unordered_set; +} // namespace metered +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_METERED_UNORDERED_SET_HPP + diff --git a/src/vendor-include/realm-ios/include/realm/util/metered/vector.hpp b/src/vendor-include/realm-ios/include/realm/util/metered/vector.hpp new file mode 100644 index 000000000..cbbf177bc --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/metered/vector.hpp @@ -0,0 +1,37 @@ +/************************************************************************* + * + * REALM CONFIDENTIAL + * __________________ + * + * [2011] - [2018] 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_METERED_VECTOR_HPP +#define REALM_UTIL_METERED_VECTOR_HPP + +#include +#include + +namespace realm { +namespace util { +namespace metered { +/// Vector with metered allocation +template > +using vector = std::vector; +} // namespace metered +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_METERED_VECTOR_HPP + diff --git a/src/vendor-include/realm-ios/include/realm/util/misc_errors.hpp b/src/vendor-include/realm-ios/include/realm/util/misc_errors.hpp new file mode 100644 index 000000000..9335ba90b --- /dev/null +++ b/src/vendor-include/realm-ios/include/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/src/vendor-include/realm-ios/include/realm/util/misc_ext_errors.hpp b/src/vendor-include/realm-ios/include/realm/util/misc_ext_errors.hpp new file mode 100644 index 000000000..6b709984c --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/misc_ext_errors.hpp @@ -0,0 +1,72 @@ +/************************************************************************* + * + * 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_MISC_EXT_ERRORS_HPP +#define REALM_UTIL_MISC_EXT_ERRORS_HPP + +#include + +namespace realm { +namespace util { + +/// FIXME: The intention is that this enum will be merged into, and subsumed by +/// util::MiscErrors in `` in the core library. +enum class MiscExtErrors { + /// End of input. + end_of_input = 1, + + /// Premature end of input. That is, end of input at an unexpected, or + /// illegal place in an input stream. + premature_end_of_input, + + /// Delimiter not found. + delim_not_found, + + /// Operation not supported + operation_not_supported, +}; + +class MiscExtErrorCategory : public std::error_category { +public: + const char* name() const noexcept override final; + std::string message(int) const override final; +}; + +/// The error category associated with MiscErrors. The name of this category is +/// `realm.util.misc_ext`. +extern MiscExtErrorCategory misc_ext_error_category; + +inline std::error_code make_error_code(MiscExtErrors err) +{ + return std::error_code(int(err), misc_ext_error_category); +} + +} // namespace util +} // namespace realm + +namespace std { + +template<> class is_error_code_enum { +public: + static const bool value = true; +}; + +} // namespace std + +#endif // REALM_UTIL_MISC_EXT_ERRORS_HPP diff --git a/src/vendor-include/realm-ios/include/realm/util/miscellaneous.hpp b/src/vendor-include/realm-ios/include/realm/util/miscellaneous.hpp new file mode 100644 index 000000000..c45e4f399 --- /dev/null +++ b/src/vendor-include/realm-ios/include/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/src/vendor-include/realm-ios/include/realm/util/network.hpp b/src/vendor-include/realm-ios/include/realm/util/network.hpp new file mode 100644 index 000000000..f55aedea6 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/network.hpp @@ -0,0 +1,3708 @@ +/************************************************************************* + * + * 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 + +#ifdef _WIN32 +# include +# include +# include +# include +# pragma comment(lib, "Ws2_32.lib") +#else +# include +# include +# include +#endif + +#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__) && !REALM_ANDROID +# 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, \ref Trigger, +/// 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 Trigger; +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 Service; + 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 Service; +}; + + +/// \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 before run() + /// returns. Also, if a handler is submitted by a call to post, and that + /// call happens after stop() returns, then that handler is guaranteed to + /// not be executed before run() returns (assuming that reset() is not called + /// before run() returns). + /// + /// 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 resume 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); + + /// Argument `saturation` is the fraction of time that is not spent + /// sleeping. Argument `inefficiency` is the fraction of time not spent + /// sleeping, and not spent executing completion handlers. Both values are + /// guaranteed to always be in the range 0 to 1 (both inclusive). The value + /// passed as `inefficiency` is guaranteed to always be less than, or equal + /// to the value passed as `saturation`. + using EventLoopMetricsHandler = void(double saturation, double inefficiency); + + /// \brief Report event loop metrics via the specified handler. + /// + /// The handler will be called approximately every 30 seconds. + /// + /// report_event_loop_metrics() must be called prior to any invocation of + /// run(). report_event_loop_metrics() is not thread-safe. + /// + /// This feature is only available if + /// `REALM_UTIL_NETWORK_EVENT_LOOP_METRICS` was defined during + /// compilation. When the feature is not available, the specified handler + /// will never be called. + void report_event_loop_metrics(std::function); + +private: + enum class Want { nothing = 0, read, write }; + + template class OperQueue; + class Descriptor; + class AsyncOper; + class ResolveOperBase; + class WaitOperBase; + class TriggerExecOperBase; + 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 LendersResolveOperPtr = 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&&...); + + 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; + static void trigger_exec(Impl&, TriggerExecOperBase&) noexcept; + static void reset_trigger_exec(Impl&, TriggerExecOperBase&) noexcept; + + using clock = std::chrono::steady_clock; + + friend class Resolver; + friend class SocketBase; + friend class Socket; + friend class Acceptor; + friend class DeadlineTimer; + friend class Trigger; + 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: +#ifdef _WIN32 + using native_handle_type = SOCKET; +#else + using native_handle_type = int; +#endif + + 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(native_handle_type fd, bool in_blocking_mode) noexcept; + void close() noexcept; + native_handle_type release() 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: + native_handle_type 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; + native_handle_type do_release() 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: + template class ResolveOper; + + Service::Impl& m_service_impl; + + Service::OwnersOperPtr m_resolve_oper; + + void initiate_oper(Service::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 Service; +}; + + +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; + + /// Release the ownership of this socket object over the native handle and + /// return the native handle to the caller. The caller assumes ownership + /// over the returned handle. The socket is left in a closed + /// state. Incomplete asynchronous operations will be canceled as if close() + /// had been called. + /// + /// If called on a closed socket, this function is a no-op, and returns the + /// same value as would be returned by native_handle() + native_handle_type release_native_handle() noexcept; + +private: + enum opt_enum { + opt_ReuseAddr, ///< `SOL_SOCKET`, `SO_REUSEADDR` + opt_Linger, ///< `SOL_SOCKET`, `SO_LINGER` + opt_NoDelay, ///< `IPPROTO_TCP`, `TCP_NODELAY` (disable the Nagle algorithm) + }; + + template class Option; + +public: + using reuse_address = Option; + using no_delay = 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&, native_handle_type, std::error_code&); + 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 + /// MiscExtErrors::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 + /// MiscExtErrors::delim_not_found. Otherwise, if the end of input is + /// reached before the delimiter is found, the operation fails with + /// MiscExtErrors::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 + /// MiscExtErrors::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 MiscExtErrors::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 + /// MiscExtErrors::delim_not_found. Otherwise, if the end of input is + /// reached before a delimiter is found, the operation fails with + /// MiscExtErrors::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 { +#ifdef _WIN32 + /// Shutdown the receiving side of the socket. + shutdown_receive = SD_RECEIVE, + + /// Shutdown the sending side of the socket. + shutdown_send = SD_SEND, + + /// Shutdown both sending and receiving side of the socket. + shutdown_both = SD_BOTH +#else + shutdown_receive = SHUT_RD, + shutdown_send = SHUT_WR, + shutdown_both = SHUT_RDWR +#endif + }; + + /// @{ \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 initiate_oper(Service::LendersWaitOperPtr); +}; + + +/// \brief Register a function whose invocation can be triggered repeatedly. +/// +/// While the function is always executed by the event loop thread, the +/// triggering of its execution can be done by any thread, and the triggering +/// operation is guaranteed to never throw. +/// +/// The function is guaranteed to not be called after the Trigger object is +/// destroyed. +/// +/// It is safe to destroy the Trigger object during execution of the function. +/// +/// Note that even though the trigger() function is thread-safe, the Trigger +/// object, as a whole, is not. In particular, construction and destruction must +/// not be considered thread-safe. +/// +/// ### Relation to post() +/// +/// For a particular execution of trigger() and a particular invocation of +/// Service::post(), if the execution of trigger() ends before the execution of +/// Service::post() begins, then it is guaranteed that the function associated +/// with the trigger gets to execute at least once after the execution of +/// trigger() begins, and before the post handler gets to execute. +class Trigger { +public: + template Trigger(Service&, F func); + ~Trigger() noexcept; + + Trigger() noexcept = default; + Trigger(Trigger&&) noexcept = default; + Trigger& operator=(Trigger&&) noexcept = default; + + /// \brief Trigger another invocation of the associated function. + /// + /// An invocation of trigger() puts the Trigger object into the triggered + /// state. It remains in the triggered state until shortly before the + /// function starts to execute. While the Trigger object is in the triggered + /// state, trigger() has no effect. This means that the number of executions + /// of the function will generally be less that the number of times + /// trigger() is invoked(). + /// + /// A particular invocation of trigger() ensures that there will be at least + /// one invocation of the associated function whose execution begins after + /// the beginning of the execution of trigger(), so long as the event loop + /// thread does not exit prematurely from run(). + /// + /// If trigger() is invoked from the event loop thread, the next execution + /// of the associated function will not begin until after trigger returns(), + /// effectively preventing reentrancy for the associated function. + /// + /// If trigger() is invoked from another thread, the associated function may + /// start to execute before trigger() returns. + /// + /// Note that the associated function can retrigger itself, i.e., if the + /// associated function calls trigger(), then that will lead to another + /// invocation of the associated function, but not until the first + /// invocation ends (no reentrance). + /// + /// This function is thread-safe. + void trigger() noexcept; + +private: + template class ExecOper; + + util::bind_ptr m_exec_oper; +}; + + +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 class ResolveErrors { + /// Host not found (authoritative). + host_not_found = 1, + + /// 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 +}; + +class ResolveErrorCategory : public std::error_category { +public: + const char* name() const noexcept override final; + std::string message(int) const override final; +}; + +/// The error category associated with ResolveErrors. The name of this category is +/// `realm.util.network.resolve`. +extern ResolveErrorCategory resolve_error_category; + +inline std::error_code make_error_code(ResolveErrors err) +{ + return std::error_code(int(err), resolve_error_category); +} + +} // 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; +#ifdef _WIN32 + void* src = const_cast(reinterpret_cast(&addr.m_union)); +#else + const void* src = &addr.m_union; +#endif + const char* ret = ::inet_ntop(af, src, 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(native_handle_type 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 auto Service::Descriptor::release() noexcept -> native_handle_type +{ + REALM_ASSERT(is_open()); +#if REALM_HAVE_EPOLL || REALM_HAVE_KQUEUE + if (m_is_registered) + deregister_for_async(); + m_is_registered = false; +#endif + return do_release(); +} + +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::ResolveOperBase : public AsyncOper { +public: + ResolveOperBase(std::size_t size, Resolver& resolver, Resolver::Query query) noexcept : + AsyncOper{size, true}, + m_resolver{&resolver}, + m_query{std::move(query)} + { + } + void complete() noexcept + { + set_is_complete(true); + } + void recycle() noexcept override final + { + bool orphaned = !m_resolver; + REALM_ASSERT(orphaned); + // Note: do_recycle() commits suicide. + do_recycle(orphaned); + } + void orphan() noexcept override final + { + m_resolver = nullptr; + } +protected: + Resolver* m_resolver; + Resolver::Query m_query; + Endpoint::List m_endpoints; + std::error_code m_error_code; + friend class Service; +}; + +class Service::WaitOperBase : public AsyncOper { +public: + WaitOperBase(std::size_t size, DeadlineTimer& timer, + clock::time_point expiration_time) noexcept : + AsyncOper{size, true}, // Second argument is `in_use` + m_timer{&timer}, + m_expiration_time{expiration_time} + { + } + void complete() noexcept + { + set_is_complete(true); + } + void recycle() noexcept override final + { + bool orphaned = !m_timer; + REALM_ASSERT(orphaned); + // 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::TriggerExecOperBase : public AsyncOper, public AtomicRefCountBase { +public: + TriggerExecOperBase(Impl& service) noexcept : + AsyncOper{0, false}, // First arg is `size` (unused), second arg is `in_use` + m_service{&service} + { + } + void recycle() noexcept override final + { + REALM_ASSERT(in_use()); + REALM_ASSERT(!m_service); + // Note: Potential suicide when `self` goes out of scope + util::bind_ptr self{this, bind_ptr_base::adopt_tag{}}; + } + void orphan() noexcept override final + { + REALM_ASSERT(m_service); + m_service = nullptr; + } + void trigger() noexcept + { + REALM_ASSERT(m_service); + Service::trigger_exec(*m_service, *this); + } +protected: + Impl* m_service; +}; + +class Service::PostOperBase : public AsyncOper { +public: + PostOperBase(std::size_t size, Impl& service) noexcept : + 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 +// MiscExtErrors::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; + REALM_ASSERT(orphaned); + // 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 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 ---------------- + +template class Resolver::ResolveOper : public Service::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) +{ + Service::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 auto SocketBase::release_native_handle() noexcept -> native_handle_type +{ + if (is_open()) { + cancel(); + return m_desc.release(); + } + return m_desc.native_handle(); +} + +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; + REALM_ASSERT(orphaned); + // 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; + REALM_ASSERT(orphaned); + // 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 util::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 util::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 util::overflow_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 + initiate_oper(std::move(op)); // Throws +} + +// ---------------- Trigger ---------------- + +template +class Trigger::ExecOper : public Service::TriggerExecOperBase { +public: + ExecOper(Service::Impl& service_impl, H handler) : + Service::TriggerExecOperBase{service_impl}, + m_handler{std::move(handler)} + { + } + void recycle_and_execute() override final + { + REALM_ASSERT(in_use()); + // Note: Potential suicide when `self` goes out of scope + util::bind_ptr self{this, bind_ptr_base::adopt_tag{}}; + if (m_service) { + Service::reset_trigger_exec(*m_service, *this); + m_handler(); // Throws + } + } +private: + H m_handler; +}; + +template inline Trigger::Trigger(Service& service, H handler) : + m_exec_oper{new ExecOper{*service.m_impl, std::move(handler)}} // Throws +{ +} + +inline Trigger::~Trigger() noexcept +{ + if (m_exec_oper) + m_exec_oper->orphan(); +} + +inline void Trigger::trigger() noexcept +{ + REALM_ASSERT(m_exec_oper); + m_exec_oper->trigger(); +} + +// ---------------- 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/src/vendor-include/realm-ios/include/realm/util/network_ssl.hpp b/src/vendor-include/realm-ios/include/realm/util/network_ssl.hpp new file mode 100644 index 000000000..3284bf848 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/network_ssl.hpp @@ -0,0 +1,1378 @@ +/************************************************************************* + * + * 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 +#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 { + +enum class Errors { + certificate_rejected = 1, +}; + +class ErrorCategory : public std::error_category { +public: + const char* name() const noexcept override final; + std::string message(int) const override final; + bool equivalent(const std::error_code&, int) const noexcept override final; +}; + +/// The error category associated with \ref Errors. The name of this category is +/// `realm.util.network.ssl`. +extern ErrorCategory error_category; + +inline std::error_code make_error_code(Errors err) +{ + return std::error_code(int(err), error_category); +} + +inline std::error_condition make_error_condition(Errors err) +{ + return std::error_condition(int(err), error_category); +} + +} // namespace ssl +} // namespace network +} // namespace util +} // namespace realm + +namespace std { + +template<> class is_error_condition_enum { +public: + static const bool value = true; +}; + +} // namespace std + +namespace realm { +namespace util { +namespace network { + +class OpensslErrorCategory : public std::error_category { +public: + const char* name() const noexcept override final; + std::string message(int) const override final; +}; + +/// The error category associated with error codes produced by the third-party +/// library, OpenSSL. The name of this category is `openssl`. +extern OpensslErrorCategory openssl_error_category; + +class SecureTransportErrorCategory : public std::error_category { +public: + const char* name() const noexcept override final; + std::string message(int) const override final; +}; + +/// The error category associated with error codes produced by Apple's +/// SecureTransport library. The name of this category is `securetransport`. +extern SecureTransportErrorCategory secure_transport_error_category; + + +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 certificate. If use_verify_file() + /// is not called, the default device trust store will be used. + /// use_verify_file() 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 + SSL_CTX* m_ssl_ctx = nullptr; + +#elif REALM_HAVE_SECURE_TRANSPORT + +#if REALM_HAVE_KEYCHAIN_APIS + 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; + +#else + using SecKeychainRef = std::nullptr_t; + +#endif // REALM_HAVE_KEYCHAIN_APIS + static util::CFPtr load_pem_file(const std::string& path, SecKeychainRef, std::error_code&); + + util::CFPtr m_trust_anchors; + util::CFPtr m_pinned_certificate; + +#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: + using port_type = util::network::Endpoint::port_type; + using SSLVerifyCallback = bool(const std::string& server_address, + port_type server_port, + const char* pem_data, + size_t pem_size, + int preverify_ok, + int depth); + + enum HandshakeType { client, server }; + + util::Logger* logger = nullptr; + + Stream(Socket&, Context&, HandshakeType); + ~Stream() noexcept; + + /// \brief set_logger() set a logger for the stream class. If + /// set_logger() is not called, no logging will take place by + /// the Stream class. + void set_logger(util::Logger*); + + /// \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); + const std::string& get_host_name(); + + /// get_server_port() and set_server_port() are getter and setter for + /// the server port. They are only used by the verify callback function + /// below. + port_type get_server_port(); + void set_server_port(port_type server_port); + + /// If use_verify_callback() is called, the SSL certificate chain of + /// the server is presented to callback, one certificate at a time. + /// The SSL connection is accepted if and only if callback returns true + /// for all certificates. + /// The signature of \param callback is + /// + /// bool(const std::string& server_address, + /// port_type server_port, + /// const char* pem_data, + /// size_t pem_size, + /// int preverify_ok, + /// int depth); + // + /// server address and server_port is the address and port of the server + /// that a SSL connection is being established to. + /// pem_data is the certificate of length pem_size in + /// the PEM format. preverify_ok is OpenSSL's preverification of the + /// certificate. preverify_ok is either 0, or 1. If preverify_ok is 1, + /// OpenSSL has accepted the certificate and it will generally be safe + /// to trust that certificate. depth represents the position of the + /// certificate in the certificate chain sent by the server. depth = 0 + /// represents the actual server certificate that should contain the + /// host name(server address) of the server. The highest depth is the + /// root certificate. + /// The callback function will receive the certificates starting from + /// the root certificate and moving down the chain until it reaches the + /// server's own certificate with a host name. The depth of the last + /// certificate is 0. The depth of the first certificate is chain + /// length - 1. + /// + /// The return value of the callback function decides whether the + /// client accepts the certificate. If the return value is false, the + /// processing of the certificate chain is interrupted and the SSL + /// connection is rejected. If the return value is true, the verification + /// process continues. If the callback function returns true for all + /// presented certificates including the depth == 0 certificate, the + /// SSL connection is accepted. + /// + /// A recommended way of using the callback function is to return true + /// if preverify_ok = 1 and depth > 0, + /// always check the host name if depth = 0, + /// and use an independent verification step if preverify_ok = 0. + /// + /// Another possible way of using the callback is to collect all the + /// certificates until depth = 0, and present the entire chain for + /// independent verification. + void use_verify_callback(const std::function& callback); + +#ifdef REALM_INCLUDE_CERTS + /// use_included_certificates() loads a set of certificates that are + /// included in the header file src/realm/noinst/root_certs.hpp. By using + /// the included certificates, the client can verify a server in the case + /// where the relevant certificate cannot be found, or is absent, in the + /// system trust store. This function is only implemented for OpenSSL. + void use_included_certificates(); +#endif + + /// @{ + /// + /// 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 successfully 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 initiated. 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. + // The host name is called server address in the certificate verify + // callback function. + std::string m_host_name; + + // The port of the server which is used in the certificate verify + // callback function. + port_type m_server_port; + + // The callback for certificate verification and an + // opaque argument that will be supplied to the callback. + const std::function* m_ssl_verify_callback = nullptr; + + bool m_valid_certificate_in_chain = false; + + + // 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 different + // 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 higher 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_use_verify_callback(const std::function&, std::error_code&); + void ssl_use_included_certificates(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; + + int m_ssl_index = -1; + + 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; + + // verify_callback_using_hostname is used as an argument to OpenSSL's SSL_set_verify function. + // verify_callback_using_hostname verifies that the certificate is valid and contains + // m_host_name as a Common Name or Subject Alternative Name. + static int verify_callback_using_hostname(int preverify_ok, X509_STORE_CTX *ctx) noexcept; + + // verify_callback_using_delegate() is also used as an argument to OpenSSL's set_verify_function. + // verify_callback_using_delegate() calls out to the user supplied verify callback. + static int verify_callback_using_delegate(int preverify_ok, X509_STORE_CTX *ctx) noexcept; + + // verify_callback_using_root_certs is used by OpenSSL to handle certificate verification + // using the included root certifictes. + static int verify_callback_using_root_certs(int preverify_ok, X509_STORE_CTX *ctx); +#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; + REALM_ASSERT(orphaned); + // 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; + REALM_ASSERT(orphaned); + // 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_logger(util::Logger* logger) +{ + this->logger = logger; +} + +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) +{ + m_host_name = host_name; + std::error_code ec; + ssl_set_check_host(host_name, ec); + if (ec) + throw std::system_error(ec); +} + +inline const std::string& Stream::get_host_name() +{ + return m_host_name; +} + +inline Stream::port_type Stream::get_server_port() +{ + return m_server_port; +} + +inline void Stream::set_server_port(port_type server_port) +{ + m_server_port = server_port; +} + +inline void Stream::use_verify_callback(const std::function& callback) +{ + std::error_code ec; + ssl_use_verify_callback(callback, ec); // Throws + if (ec) + throw std::system_error(ec); +} + +#ifdef REALM_INCLUDE_CERTS +inline void Stream::use_included_certificates() +{ + std::error_code ec; + ssl_use_included_certificates(ec); // Throws + if (ec) + throw std::system_error(ec); +} +#endif + +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 = MiscExtErrors::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 = MiscExtErrors::end_of_input; + } + else { + ec = MiscExtErrors::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 = MiscExtErrors::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 = MiscExtErrors::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 need 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 documentation 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 + // MiscExtErrors::premature_end_of_input. + // If we see this error case occurring in other situations in + // the future, we will have to update this case. + ec = MiscExtErrors::premature_end_of_input; + } + want = Want::nothing; + return 0; + case SSL_ERROR_SSL: + ec = std::error_code(sys_error, 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 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. +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 = MiscExtErrors::end_of_input; + want = Want::nothing; + return n; + } + + if (result == errSSLClosedAbort || result == errSSLClosedNoNotify) { + ec = MiscExtErrors::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, 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/src/vendor-include/realm-ios/include/realm/util/optional.hpp b/src/vendor-include/realm-ios/include/realm/util/optional.hpp new file mode 100644 index 000000000..446b9720d --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/optional.hpp @@ -0,0 +1,734 @@ +/************************************************************************* + * + * 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 + +#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 : ExceptionWithBacktrace { + using ExceptionWithBacktrace::ExceptionWithBacktrace; +}; + +} // 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) noexcept; + 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) noexcept; + Optional& operator=(Optional&& other) noexcept(std::is_nothrow_move_assignable::value); + Optional& operator=(const Optional& other) noexcept(std::is_nothrow_copy_assignable::value); + + 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) noexcept + : m_ptr(other.m_ptr) + { + } + template + Optional(std::reference_wrapper ref) noexcept + : m_ptr(&ref.get()) + { + } + + constexpr Optional(T& init_value) noexcept + : m_ptr(&init_value) + { + } + Optional(T&& value) = delete; // Catches accidental references to rvalue temporaries. + + Optional& operator=(None) noexcept + { + m_ptr = nullptr; + return *this; + } + Optional& operator=(const Optional& other) + { + m_ptr = other.m_ptr; + return *this; + } + + template + Optional& operator=(std::reference_wrapper ref) noexcept + { + m_ptr = &ref.get(); + return *this; + } + + explicit constexpr operator bool() const noexcept + { + 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) noexcept + : 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) noexcept +{ + clear(); + return *this; +} + +template +Optional& Optional::operator=(Optional&& other) noexcept(std::is_nothrow_move_assignable::value) +{ + 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) noexcept(std::is_nothrow_copy_assignable::value) +{ + 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_value; +} + +template +T& Optional::value() +{ + REALM_ASSERT(m_engaged); + return m_value; +} + +template +constexpr const typename Optional::target_type& Optional::value() const +{ + return *m_ptr; +} + +template +typename Optional::target_type& Optional::value() +{ + REALM_ASSERT(m_ptr); + return *m_ptr; +} + +template +constexpr const T& Optional::operator*() const +{ + return value(); +} + +template +T& Optional::operator*() +{ + return value(); +} + +template +constexpr const T* Optional::operator->() const +{ + return &value(); +} + +template +T* Optional::operator->() +{ + 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 + + +// for convienence, inject a default hash implementation into the std namespace +namespace std +{ + template + struct hash> + { + std::size_t operator()(realm::util::Optional const& o) const noexcept + { + if (bool(o) == false) { + return 0; // any choice will collide with some std::hash + } else { + return std::hash{}(*o); + } + } + }; +} + + +#endif // REALM_UTIL_OPTIONAL_HPP diff --git a/src/vendor-include/realm-ios/include/realm/util/overload.hpp b/src/vendor-include/realm-ios/include/realm/util/overload.hpp new file mode 100644 index 000000000..49188ae13 --- /dev/null +++ b/src/vendor-include/realm-ios/include/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/src/vendor-include/realm-ios/include/realm/util/parent_dir.hpp b/src/vendor-include/realm-ios/include/realm/util/parent_dir.hpp new file mode 100644 index 000000000..0e74e2275 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/parent_dir.hpp @@ -0,0 +1,37 @@ +/************************************************************************* + * + * 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_PARENT_DIR_HPP +#define REALM_UTIL_PARENT_DIR_HPP + +#include + +namespace realm { +namespace util { + +/// Same effect as std::filesystem::path::parent_path(). +/// +/// FIXME: This function ought to be moved to in the +/// realm-core repository. +std::string parent_dir(const std::string& path); + +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_PARENT_DIR_HPP diff --git a/src/vendor-include/realm-ios/include/realm/util/platform_info.hpp b/src/vendor-include/realm-ios/include/realm/util/platform_info.hpp new file mode 100644 index 000000000..16ae43a4b --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/platform_info.hpp @@ -0,0 +1,64 @@ +/************************************************************************* + * + * 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_PLATFORM_INFO_HPP +#define REALM_UTIL_PLATFORM_INFO_HPP + +#include + + +namespace realm { +namespace util { + +/// Get a description of the current system platform. +/// +/// Returns a space-separated concatenation of `osname`, `sysname`, `release`, +/// `version`, and `machine` as returned by get_platform_info(PlatformInfo&). +std::string get_platform_info(); + + +struct PlatformInfo { + std::string osname; ///< Equivalent to `uname -o` (Linux). + std::string sysname; ///< Equivalent to `uname -s`. + std::string release; ///< Equivalent to `uname -r`. + std::string version; ///< Equivalent to `uname -v`. + std::string machine; ///< Equivalent to `uname -m`. +}; + +/// Get a description of the current system platform. +void get_platform_info(PlatformInfo&); + + + + +// Implementation + +inline std::string get_platform_info() +{ + PlatformInfo info; + get_platform_info(info); // Throws + return (info.osname + " " + info.sysname + " " + info.release + " " + info.version + " " + + info.machine); // Throws +} + + +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_PLATFORM_INFO_HPP diff --git a/src/vendor-include/realm-ios/include/realm/util/priority_queue.hpp b/src/vendor-include/realm-ios/include/realm/util/priority_queue.hpp new file mode 100644 index 000000000..a2a28c89d --- /dev/null +++ b/src/vendor-include/realm-ios/include/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/src/vendor-include/realm-ios/include/realm/util/quote.hpp b/src/vendor-include/realm-ios/include/realm/util/quote.hpp new file mode 100644 index 000000000..7959a4459 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/quote.hpp @@ -0,0 +1,175 @@ +/************************************************************************* + * + * 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_QUOTE_HPP +#define REALM_UTIL_QUOTE_HPP + +#include + +namespace realm { +namespace util { + +template struct Quote { + bool smart; + util::BasicStringView view; +}; + + +/// Mark text for quotation during output to stream. +/// +/// If `out` is an output stream, and `str` is a string (e.g., an std::string), +/// then +/// +/// out << quoted(str) +/// +/// will write `str` in quoted form to `out`. +/// +/// Quotation involves bracketing the text in double quotes (`"`), and escaping +/// special characters according to the rules of C/C++ string literals. In this +/// case, the special characters are `"` and `\` as well as those that are not +/// printable (!std::isprint()). +/// +/// Quotation happens as the string is written to a stream, so there is no +/// intermediate representation of the quoted string. +template Quote quoted(util::BasicStringView) noexcept; + + +/// Same as quoted(), except that in this case, quotation is elided when the +/// specified string consists of a single printable word. Or, to be more +/// precise, quotation is elided if the string is nonempty, consists entirely of +/// printable charcters (std::isprint()), does not contain space (` `), and does +/// not conatian quotation (`"`) or backslash (`\`). +template Quote smart_quoted(util::BasicStringView) noexcept; + + +template +std::basic_ostream& operator<<(std::basic_ostream&, Quote); + + + + + +// Implementation + +template inline Quote quoted(util::BasicStringView view) noexcept +{ + bool smart = false; + return {smart, view}; +} + +template +inline Quote smart_quoted(util::BasicStringView view) noexcept +{ + bool smart = true; + return {smart, view}; +} + +template +inline std::basic_ostream& operator<<(std::basic_ostream& out, Quote quoted) +{ + std::locale loc = out.getloc(); + const std::ctype& ctype = std::use_facet>(loc); + util::BasicStringView view = quoted.view; + if (quoted.smart && !view.empty()) { + for (C ch : view) { + if (ch == '"' || ch == '\\' || !ctype.is(ctype.graph, ch)) + goto quote; + } + return out << view; // Throws + } + quote: + typename std::basic_ostream::sentry sentry{out}; + if (REALM_LIKELY(sentry)) { + C dquote = ctype.widen('"'); + C bslash = ctype.widen('\\'); + out.put(dquote); // Throws + bool follows_hex = false; + for (C ch : view) { + if (REALM_LIKELY(ctype.is(ctype.print, ch))) { + if (REALM_LIKELY(!follows_hex || !ctype.is(ctype.xdigit, ch))) { + if (REALM_LIKELY(ch != '"' || ch != '\\')) + goto put_char; + goto escape_char; + } + } + switch (ch) { + case '\a': + ch = ctype.widen('a'); + goto escape_char; + case '\b': + ch = ctype.widen('b'); + goto escape_char; + case '\f': + ch = ctype.widen('f'); + goto escape_char; + case '\n': + ch = ctype.widen('n'); + goto escape_char; + case '\r': + ch = ctype.widen('r'); + goto escape_char; + case '\t': + ch = ctype.widen('t'); + goto escape_char; + case '\v': + ch = ctype.widen('v'); + goto escape_char; + } + goto numeric; + escape_char: + out.put(bslash); // Throws + put_char: + out.put(ch); // Throws + next: + follows_hex = false; + continue; + numeric: + out.put(bslash); // Throws + using D = typename std::make_unsigned::type; + char digits[] = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' + }; + D val = ch; + if (val < 512) { + out.put(ctype.widen(digits[val / 64 ])); // Throws + out.put(ctype.widen(digits[val % 64 / 8])); // Throws + out.put(ctype.widen(digits[val % 8])); // Throws + goto next; + } + out.put(ctype.widen('x')); // Throws + const int max_hex_digits = (std::numeric_limits::digits + 3) / 4; + C buffer[max_hex_digits]; + int i = max_hex_digits; + while (val != 0) { + buffer[--i] = ctype.widen(digits[val % 16]); + val /= 16; + } + out.write(buffer + i, max_hex_digits - i); // Throws + follows_hex = true; + } + out.put(dquote); // Throws + } + return out; +} + + +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_QUOTE_HPP diff --git a/src/vendor-include/realm-ios/include/realm/util/random.hpp b/src/vendor-include/realm-ios/include/realm/util/random.hpp new file mode 100644 index 000000000..d0c7fe933 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/random.hpp @@ -0,0 +1,137 @@ +/************************************************************************* + * + * 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&); + +template +std::string generate_random_lower_case_string(Engine& engine, size_t size); + + +// 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); +} + +template +std::string generate_random_lower_case_string(Engine& engine, size_t size) +{ + std::uniform_int_distribution dist(0, 25); + std::string str; + str.reserve(size); + for (size_t i = 0; i < size; ++i) { + short val = dist(engine); + char c = 'a' + char(val); + str.push_back(c); + } + return str; +} + +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_RANDOM_HPP diff --git a/src/vendor-include/realm-ios/include/realm/util/resource_limits.hpp b/src/vendor-include/realm-ios/include/realm/util/resource_limits.hpp new file mode 100644 index 000000000..858624aa5 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/resource_limits.hpp @@ -0,0 +1,83 @@ +/************************************************************************* + * + * 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_RESOURCE_LIMITS_HPP +#define REALM_UTIL_RESOURCE_LIMITS_HPP + +namespace realm { +namespace util { + + +enum class Resource { + /// The maximum size, in bytes, of the core file produced when the memory + /// image of this process is dumped. If the memory image is larger than the + /// limit, the core file will not be created. Same as `RLIMIT_CORE` of + /// POSIX. + core_dump_size, + + /// The maximum CPU time, in seconds, available to this process. If the + /// limit is exceeded, the process will be killed. Same as `RLIMIT_CPU` of + /// POSIX. + cpu_time, + + /// The maximum size, in bytes, of the data segment of this process. If the + /// limit is exceede, std::malloc() will fail with `errno` equal to + /// `ENOMEM`. Same as `RLIMIT_DATA` of POSIX. + data_segment_size, + + /// The maximum size, in bytes, of a file that is modified by this + /// process. If the limit is exceede, the process will be killed. Same as + /// `RLIMIT_FSIZE` of POSIX. + file_size, + + /// One plus the maximum file descriptor value that can be opened by this + /// process. Same as `RLIMIT_NOFILE` of POSIX. + num_open_files, + + /// The maximum size, in bytes, of the stack of the main thread of this + /// process. If the limit is exceede, the process is killed. Same as + /// `RLIMIT_STACK` of POSIX. + stack_size, + + /// The maximum size, in bytes, of the process's virtual memory (address + /// space). If the limit is exceeded due to heap allocation, std::malloc() + /// will fail with `errno` equal to `ENOMEM`. If the limit is exceeded due + /// to explicit memory mapping, mmap() will fail with `errno` equal to + /// `ENOMEM`. If the limit is exceeded due to stack expansion, the process + /// will be killed. Same as `RLIMIT_AS` of POSIX. + virtual_memory_size +}; + + +bool system_has_rlimit(Resource) noexcept; + + +//@{ +/// Get or set resouce limits. A negative value means 'unlimited', both when +/// getting and when setting. +long get_hard_rlimit(Resource); +long get_soft_rlimit(Resource); +void set_soft_rlimit(Resource, long value); +//@} + + +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_RESOURCE_LIMITS_HPP diff --git a/src/vendor-include/realm-ios/include/realm/util/safe_int_ops.hpp b/src/vendor-include/realm-ios/include/realm/util/safe_int_ops.hpp new file mode 100644 index 000000000..15030add4 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/safe_int_ops.hpp @@ -0,0 +1,623 @@ +/************************************************************************* + * + * 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. In particular, the +/// value of i must not exceed the number of bits of storage type T as +/// shifting by this amount is not defined by the 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_l::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/src/vendor-include/realm-ios/include/realm/util/scope_exit.hpp b/src/vendor-include/realm-ios/include/realm/util/scope_exit.hpp new file mode 100644 index 000000000..5410d1957 --- /dev/null +++ b/src/vendor-include/realm-ios/include/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/src/vendor-include/realm-ios/include/realm/util/serializer.hpp b/src/vendor-include/realm-ios/include/realm/util/serializer.hpp new file mode 100644 index 000000000..fb998430d --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/serializer.hpp @@ -0,0 +1,93 @@ +/************************************************************************* + * + * 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_SERIALIZER_HPP +#define REALM_UTIL_SERIALIZER_HPP + +#include +#include + +#include +#include +#include + +namespace realm { + +class BinaryData; +struct ColKey; +struct null; +struct ObjKey; +class StringData; +class Timestamp; +class LinkMap; + +namespace util { +namespace serializer { + + +// Definitions +template +std::string print_value(T value); + +template +std::string print_value(Optional value); + +const static std::string value_separator = "."; + +// Specializations declared here to be defined in the cpp file +template <> std::string print_value<>(BinaryData); +template <> std::string print_value<>(bool); +template <> std::string print_value<>(realm::null); +template <> std::string print_value<>(StringData); +template <> std::string print_value<>(realm::Timestamp); +template <> +std::string print_value<>(realm::ObjKey); + +// General implementation for most types +template +std::string print_value(T value) +{ + std::stringstream ss; + ss << value; + return ss.str(); +} + +template +std::string print_value(Optional value) +{ + if (bool(value)) { + return print_value(*value); + } else { + return "NULL"; + } +} + +struct SerialisationState { + std::string describe_column(ConstTableRef table, ColKey col_key); + std::string describe_columns(const LinkMap& link_map, ColKey target_col_key); + std::string get_column_name(ConstTableRef table, ColKey col_key); + std::string get_backlink_column_name(ConstTableRef from, ColKey col_key); + std::string get_variable_name(ConstTableRef table); + std::vector subquery_prefix_list; +}; + +} // namespace serializer +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_SERIALIZER_HPP diff --git a/src/vendor-include/realm-ios/include/realm/util/sha_crypto.hpp b/src/vendor-include/realm-ios/include/realm/util/sha_crypto.hpp new file mode 100644 index 000000000..324e1d431 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/sha_crypto.hpp @@ -0,0 +1,43 @@ +/************************************************************************* + * + * Copyright 2019 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_SHA_CRYPTO_HPP +#define REALM_SHA_CRYPTO_HPP + +#include + +namespace realm { +namespace util { + +/// The digest functions calculate the message digest of the input in \param +/// in_buffer of size \param in_buffer_size. The digest is placed in \param +/// out_buffer. The caller must guarantee that the output buffer is large +/// enough to contain the digest. +/// +/// The functions throw if the underlying platform dependent implementations +/// throw. Typically, exceptions are "out of memory" errors. +/// +/// sha1() calculates the SHA-1 hash value of output size 20. +/// sha256() calculates the SHA-256 hash value of output size 32. +void sha1(const char* in_buffer, size_t in_buffer_size, unsigned char* out_buffer); +void sha256(const char* in_buffer, size_t in_buffer_size, unsigned char* out_buffer); + +} // namespace util +} // namespace realm + +#endif // REALM_SHA_CRYPTO_HPP diff --git a/src/vendor-include/realm-ios/include/realm/util/shared_ptr.hpp b/src/vendor-include/realm-ios/include/realm/util/shared_ptr.hpp new file mode 100644 index 000000000..1a347017b --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/shared_ptr.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_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) + if (this == &o) + 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/src/vendor-include/realm-ios/include/realm/util/signal_blocker.hpp b/src/vendor-include/realm-ios/include/realm/util/signal_blocker.hpp new file mode 100644 index 000000000..bb1489b06 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/signal_blocker.hpp @@ -0,0 +1,79 @@ +/************************************************************************* + * + * 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_SIGNAL_BLOCKER_HPP +#define REALM_UTIL_SIGNAL_BLOCKER_HPP + +#include + +#include + + +namespace realm { +namespace util { + +/// \brief Block all signals from being delivered to the instantiating thread. +/// +/// On platforms that support POSIX signals, the constructor will set the signal +/// mask such that all signals are blocked from being delivered to the calling +/// thread, and the destructor will restore the signal mask to its original +/// value. +/// +/// This scheme assumes that it is always the same thread that constructs and +/// destroys a particular instance of SignalBlocker, and that, for a particular +/// thread, two SignalBlocker objects never overlap in time, and the signal mask +/// is never modified by other means while a SignalBlocker object exists. +class SignalBlocker { +public: + SignalBlocker() noexcept; + ~SignalBlocker() noexcept; + +private: +#ifndef _WIN32 + ::sigset_t m_orig_mask; +#endif +}; + + + +// Implementation + +inline SignalBlocker::SignalBlocker() noexcept +{ +#ifndef _WIN32 + ::sigset_t mask; + sigfillset(&mask); + int ret = ::pthread_sigmask(SIG_BLOCK, &mask, &m_orig_mask); + REALM_ASSERT(ret == 0); +#endif +} + +inline SignalBlocker::~SignalBlocker() noexcept +{ +#ifndef _WIN32 + int ret = ::pthread_sigmask(SIG_SETMASK, &m_orig_mask, nullptr); + REALM_ASSERT(ret == 0); +#endif +} + +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_SIGNAL_BLOCKER_HPP diff --git a/src/vendor-include/realm-ios/include/realm/util/string_buffer.hpp b/src/vendor-include/realm-ios/include/realm/util/string_buffer.hpp new file mode 100644 index 000000000..71736bdcf --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/string_buffer.hpp @@ -0,0 +1,209 @@ +/************************************************************************* + * + * 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: In C++17, this can be replaced with std::string (since +// std::string::data() can return a mutable pointer in C++17). +template +class BasicStringBuffer { +public: + BasicStringBuffer() 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); +}; + +using StringBuffer = BasicStringBuffer; + + +// Implementation: + +template +BasicStringBuffer::BasicStringBuffer() noexcept + : m_size(0) +{ +} + +template +std::string BasicStringBuffer::str() const +{ + return std::string(m_buffer.data(), m_size); +} + +template +size_t BasicStringBuffer::size() const noexcept +{ + return m_size; +} + +template +char* BasicStringBuffer::data() noexcept +{ + return m_buffer.data(); +} + +template +const char* BasicStringBuffer::data() const noexcept +{ + return m_buffer.data(); +} + +template +const char* BasicStringBuffer::c_str() const noexcept +{ + static const char zero = 0; + const char* d = data(); + return d ? d : &zero; +} + +template +void BasicStringBuffer::append(const std::string& s) +{ + return append(s.data(), s.size()); +} + +template +void BasicStringBuffer::append_c_str(const char* c_string) +{ + append(c_string, std::strlen(c_string)); +} + +template +void BasicStringBuffer::reserve(size_t min_capacity) +{ + size_t capacity = m_buffer.size(); + if (capacity == 0 || capacity - 1 < min_capacity) + reallocate(min_capacity); +} + +template +void BasicStringBuffer::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; +} + +template +void BasicStringBuffer::clear() noexcept +{ + if (m_buffer.size() == 0) + return; + m_size = 0; + m_buffer[0] = 0; +} + +template +void BasicStringBuffer::append(const char* append_data, size_t append_data_size) +{ + size_t new_size = m_size; + if (int_add_with_overflow_detect(new_size, append_data_size)) + throw util::BufferSizeOverflow(); + reserve(new_size); // Throws + realm::safe_copy_n(append_data, append_data_size, m_buffer.data() + m_size); + m_size = new_size; + m_buffer[new_size] = 0; // Add zero termination +} + + +template +void BasicStringBuffer::reallocate(size_t min_capacity) +{ + size_t min_capacity_2 = min_capacity; + // Make space for zero termination + if (int_add_with_overflow_detect(min_capacity_2, 1)) + throw util::BufferSizeOverflow(); + size_t new_capacity = m_buffer.size(); + if (int_multiply_with_overflow_detect(new_capacity, 2)) + new_capacity = std::numeric_limits::max(); // LCOV_EXCL_LINE + if (new_capacity < min_capacity_2) + new_capacity = min_capacity_2; + m_buffer.resize(new_capacity, 0, m_size, 0); // Throws +} + +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_STRING_BUFFER_HPP diff --git a/src/vendor-include/realm-ios/include/realm/util/string_view.hpp b/src/vendor-include/realm-ios/include/realm/util/string_view.hpp new file mode 100644 index 000000000..74fdcb6d5 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/string_view.hpp @@ -0,0 +1,478 @@ +/************************************************************************* + * + * 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_STRING_VIEW_HPP +#define REALM_UTIL_STRING_VIEW_HPP + +#include +#include +#include +#include +#include +#include +#include + +#include + + +namespace realm { +namespace util { + +template> class BasicStringView { +public: + using value_type = C; + using traits_type = T; + using pointer = C*; + using const_pointer = const C*; + using reference = C&; + using const_reference = const C&; + using iterator = const_pointer; + using const_iterator = const_pointer; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + + static constexpr size_type npos = size_type(-1); + + BasicStringView() noexcept; + BasicStringView(const std::basic_string&) noexcept; + BasicStringView(const char* data, size_type size) noexcept; + BasicStringView(const char* c_str) noexcept; + + explicit operator std::basic_string() const; + + const_iterator begin() const noexcept; + const_iterator end() const noexcept; + const_iterator cbegin() const noexcept; + const_iterator cend() const noexcept; + + const_reverse_iterator rbegin() const noexcept; + const_reverse_iterator rend() const noexcept; + const_reverse_iterator crbegin() const noexcept; + const_reverse_iterator crend() const noexcept; + + const_reference operator[](size_type i) const noexcept; + const_reference at(size_type i) const; + const_reference front() const noexcept; + const_reference back() const noexcept; + + const_pointer data() const noexcept; + size_type size() const noexcept; + bool empty() const noexcept; + + BasicStringView substr(size_type i = 0, size_type n = npos) const; + int compare(BasicStringView other) const noexcept; + size_type find(BasicStringView, size_type i = 0) const noexcept; + size_type find(C ch, size_type i = 0) const noexcept; + size_type find_first_of(BasicStringView, size_type i = 0) const noexcept; + size_type find_first_of(C ch, size_type i = 0) const noexcept; + size_type find_first_not_of(BasicStringView, size_type i = 0) const noexcept; + size_type find_first_not_of(C ch, size_type i = 0) const noexcept; + +private: + const char* m_data = nullptr; + std::size_t m_size = 0; +}; + +template bool operator==(BasicStringView, BasicStringView) noexcept; +template bool operator!=(BasicStringView, BasicStringView) noexcept; +template bool operator< (BasicStringView, BasicStringView) noexcept; +template bool operator> (BasicStringView, BasicStringView) noexcept; +template bool operator<=(BasicStringView, BasicStringView) noexcept; +template bool operator>=(BasicStringView, BasicStringView) noexcept; + +template +bool operator==(std::decay_t>, BasicStringView) noexcept; +template +bool operator!=(std::decay_t>, BasicStringView) noexcept; +template +bool operator< (std::decay_t>, BasicStringView) noexcept; +template +bool operator> (std::decay_t>, BasicStringView) noexcept; +template +bool operator<=(std::decay_t>, BasicStringView) noexcept; +template +bool operator>=(std::decay_t>, BasicStringView) noexcept; + +template +bool operator==(BasicStringView, std::decay_t>) noexcept; +template +bool operator!=(BasicStringView, std::decay_t>) noexcept; +template +bool operator< (BasicStringView, std::decay_t>) noexcept; +template +bool operator> (BasicStringView, std::decay_t>) noexcept; +template +bool operator<=(BasicStringView, std::decay_t>) noexcept; +template +bool operator>=(BasicStringView, std::decay_t>) noexcept; + + +template +std::basic_ostream& operator<<(std::basic_ostream&, BasicStringView); + + +using StringView = BasicStringView; + + + + + +// Implementation + +template +inline BasicStringView::BasicStringView() noexcept +{ +} + +template +inline BasicStringView::BasicStringView(const std::basic_string& str) noexcept : + m_data{str.data()}, + m_size{str.size()} +{ +} + +template +inline BasicStringView::BasicStringView(const char* data, size_type size) noexcept : + m_data{data}, + m_size{size} +{ +} + +template +inline BasicStringView::BasicStringView(const char* c_str) noexcept : + m_data{c_str}, + m_size{T::length(c_str)} +{ +} + +template +inline BasicStringView::operator std::basic_string() const +{ + return {m_data, m_size}; // Throws +} + +template +inline auto BasicStringView::begin() const noexcept -> const_iterator +{ + return m_data; +} + +template +inline auto BasicStringView::end() const noexcept -> const_iterator +{ + return m_data + m_size; +} + +template +inline auto BasicStringView::cbegin() const noexcept -> const_iterator +{ + return begin(); +} + +template +inline auto BasicStringView::cend() const noexcept -> const_iterator +{ + return end(); +} + +template +inline auto BasicStringView::rbegin() const noexcept -> const_reverse_iterator +{ + return const_reverse_iterator{end()}; +} + +template +inline auto BasicStringView::rend() const noexcept -> const_reverse_iterator +{ + return const_reverse_iterator{begin()}; +} + +template +inline auto BasicStringView::crbegin() const noexcept -> const_reverse_iterator +{ + return rbegin(); +} + +template +inline auto BasicStringView::crend() const noexcept -> const_reverse_iterator +{ + return rend(); +} + +template +inline auto BasicStringView::operator[](size_type i) const noexcept -> const_reference +{ + return m_data[i]; +} + +template +inline auto BasicStringView::at(size_type i) const -> const_reference +{ + if (REALM_LIKELY(i < m_size)) + return m_data[i]; + throw std::out_of_range("index"); +} + +template +inline auto BasicStringView::front() const noexcept -> const_reference +{ + return m_data[0]; +} + +template +inline auto BasicStringView::back() const noexcept -> const_reference +{ + return m_data[m_size - 1]; +} + +template +inline auto BasicStringView::data() const noexcept -> const_pointer +{ + return m_data; +} + +template +inline auto BasicStringView::size() const noexcept -> size_type +{ + return m_size; +} + +template +inline bool BasicStringView::empty() const noexcept +{ + return (size() == 0); +} + +template +inline BasicStringView BasicStringView::substr(size_type i, size_type n) const +{ + if (REALM_LIKELY(i <= m_size)) { + size_type m = std::min(n, m_size - i); + return BasicStringView{m_data + i, m}; + } + throw std::out_of_range("index"); +} + +template +inline int BasicStringView::compare(BasicStringView other) const noexcept +{ + size_type n = std::min(m_size, other.m_size); + int ret = T::compare(m_data, other.m_data, n); + if (REALM_LIKELY(ret != 0)) + return ret; + if (m_size < other.m_size) + return -1; + if (m_size > other.m_size) + return 1; + return 0; +} + +template +inline auto BasicStringView::find(BasicStringView v, size_type i) const noexcept -> + size_type +{ + if (REALM_LIKELY(!v.empty())) { + if (REALM_LIKELY(i < m_size)) { + const C* p = std::search(begin() + i, end(), v.begin(), v.end()); + if (p != end()) + return size_type(p - begin()); + } + return npos; + } + return i; +} + +template +inline auto BasicStringView::find(C ch, size_type i) const noexcept -> size_type +{ + if (REALM_LIKELY(i < m_size)) { + const C* p = std::find(begin() + i, end(), ch); + if (p != end()) + return size_type(p - begin()); + } + return npos; +} + +template +inline auto BasicStringView::find_first_of(BasicStringView v, + size_type i) const noexcept -> size_type +{ + for (size_type j = i; j < m_size; ++j) { + if (REALM_LIKELY(v.find(m_data[j]) == npos)) + continue; + return j; + } + return npos; +} + +template +inline auto BasicStringView::find_first_of(C ch, size_type i) const noexcept -> size_type +{ + for (size_type j = i; j < m_size; ++j) { + if (REALM_UNLIKELY(m_data[j] == ch)) + return j; + } + return npos; +} + +template +inline auto BasicStringView::find_first_not_of(BasicStringView v, + size_type i) const noexcept -> size_type +{ + for (size_type j = i; j < m_size; ++j) { + if (REALM_UNLIKELY(v.find(m_data[j]) == npos)) + return j; + } + return npos; +} + +template +inline auto BasicStringView::find_first_not_of(C ch, size_type i) const noexcept -> size_type +{ + for (size_type j = i; j < m_size; ++j) { + if (REALM_UNLIKELY(m_data[j] != ch)) + return j; + } + return npos; +} + +template +inline bool operator==(BasicStringView lhs, BasicStringView rhs) noexcept +{ + return (lhs.compare(rhs) == 0); +} + +template +inline bool operator!=(BasicStringView lhs, BasicStringView rhs) noexcept +{ + return (lhs.compare(rhs) != 0); +} + +template +inline bool operator<(BasicStringView lhs, BasicStringView rhs) noexcept +{ + return (lhs.compare(rhs) < 0); +} + +template +inline bool operator>(BasicStringView lhs, BasicStringView rhs) noexcept +{ + return (lhs.compare(rhs) > 0); +} + +template +inline bool operator<=(BasicStringView lhs, BasicStringView rhs) noexcept +{ + return (lhs.compare(rhs) <= 0); +} + +template +inline bool operator>=(BasicStringView lhs, BasicStringView rhs) noexcept +{ + return (lhs.compare(rhs) >= 0); +} + +template +inline bool operator==(std::decay_t> lhs, BasicStringView rhs) noexcept +{ + return (lhs.compare(rhs) == 0); +} + +template +inline bool operator!=(std::decay_t> lhs, BasicStringView rhs) noexcept +{ + return (lhs.compare(rhs) != 0); +} + +template +inline bool operator<(std::decay_t> lhs, BasicStringView rhs) noexcept +{ + return (lhs.compare(rhs) < 0); +} + +template +inline bool operator>(std::decay_t> lhs, BasicStringView rhs) noexcept +{ + return (lhs.compare(rhs) > 0); +} + +template +inline bool operator<=(std::decay_t> lhs, BasicStringView rhs) noexcept +{ + return (lhs.compare(rhs) <= 0); +} + +template +inline bool operator>=(std::decay_t> lhs, BasicStringView rhs) noexcept +{ + return (lhs.compare(rhs) >= 0); +} + +template +inline bool operator==(BasicStringView lhs, std::decay_t> rhs) noexcept +{ + return (lhs.compare(rhs) == 0); +} + +template +inline bool operator!=(BasicStringView lhs, std::decay_t> rhs) noexcept +{ + return (lhs.compare(rhs) != 0); +} + +template +inline bool operator<(BasicStringView lhs, std::decay_t> rhs) noexcept +{ + return (lhs.compare(rhs) < 0); +} + +template +inline bool operator>(BasicStringView lhs, std::decay_t> rhs) noexcept +{ + return (lhs.compare(rhs) > 0); +} + +template +inline bool operator<=(BasicStringView lhs, std::decay_t> rhs) noexcept +{ + return (lhs.compare(rhs) <= 0); +} + +template +inline bool operator>=(BasicStringView lhs, std::decay_t> rhs) noexcept +{ + return (lhs.compare(rhs) >= 0); +} + +template +inline std::basic_ostream& operator<<(std::basic_ostream& out, + BasicStringView view) +{ + typename std::basic_ostream::sentry sentry{out}; + if (REALM_LIKELY(sentry)) + out.write(view.data(), view.size()); + return out; +} + +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_STRING_VIEW_HPP diff --git a/src/vendor-include/realm-ios/include/realm/util/substitute.hpp b/src/vendor-include/realm-ios/include/realm/util/substitute.hpp new file mode 100644 index 000000000..b551f3116 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/substitute.hpp @@ -0,0 +1,373 @@ +/************************************************************************* + * + * 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_SUBSTITUTE_HPP +#define REALM_UTIL_SUBSTITUTE_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + + +namespace realm { +namespace util { +namespace _private { + +class SubstituterBase { +protected: + template struct FindArg1; + template struct FindArg2; + static StderrLogger s_default_logger; +}; + +} // namespace _private + + +struct SubstituterConfig { + /// Allow parsing to be considered successful even when syntax errors are + /// detected. When enabled, logging will happen on `warn`, instead of + /// `error` level. + bool lenient = false; + + /// The logger to be used by default. If left unspecified, the default + /// logger is one that logs to STDERR. In any case, logging happens only + /// during parsing. + Logger* logger = nullptr; +}; + + +/// Perform variable substitutions in text. +/// +/// A variable reference generally has the form `@{}`, where `` is +/// the variable name. For example, if the variable name is `x`, then `@{x}` is +/// a reference to that variable. If the variable name consists of a single +/// letter, then a shorter form of reference, `@` is available, i.e., +/// since `x` is a single letter, `@x` is a reference to `x`. As a special rule, +/// `@@` is substituted by `@`. +/// +/// Example of use: +/// +/// struct CtxA { int y = 0; }; +/// struct CtxB { int x = 0; }; +/// using Subst = Substituter; +/// Subst subst; +/// subst["x"] = &CtxB::x; +/// subst["y"] = [](std::ostream& out, const CtxA& a, const CtxB&) { +/// out << a.y; +/// }; +/// Subst::Template templ; +/// if (subst.parse("<@x:@y>\n", templ)) { +/// CtxA a; +/// CtxB b; +/// for (int i = 0; i < 3; ++i) { +/// templ.expand(std::cout, a, b); +/// a.y += 1; +/// b.x += 2; +/// } +/// } +/// +/// This code should write +/// +/// <0:0> +/// <2:1> +/// <4:2> +/// +/// to STDOUT. +template class Substituter : private _private::SubstituterBase { +public: + using EvalFunc = void(std::ostream&, A&...); + class ProtoDef; + class Template; + + Substituter(SubstituterConfig = {}) noexcept; + + ProtoDef operator[](const char* name) noexcept; + + bool expand(StringView text, std::ostream&, A&&...) const; + + bool parse(StringView text, Template&) const; + bool parse(StringView text, Template&, Logger&) const; + +private: + using size_type = StringView::size_type; + struct Substitution; + + const bool m_lenient; + Logger& m_logger; + + using Variables = std::map>; + Variables m_variables; + + void define(const char* name, std::function); +}; + + + +template class Substituter::ProtoDef { +public: + template void operator=(T*); + template void operator=(T C::*); + void operator=(std::function); + +private: + Substituter& m_substituter; + const char* m_name; + + ProtoDef(Substituter& substituter, const char* name) noexcept; + + friend class Substituter; +}; + + + +template class Substituter::Template { +public: + /// Uses std::locale::classic(). + std::string expand(A&&...) const; + + void expand(std::ostream&, A...) const; + + bool refers_to(const char* name) const noexcept; + +private: + StringView m_text; + std::vector m_substitutions; + + friend class Substituter; +}; + + + + + +// Implementation + +namespace _private { + +template struct SubstituterBase::FindArg2 { + static const T& find(const A&, const B&... b) noexcept + { + return FindArg1::find(b...); + } +}; + +template +struct SubstituterBase::FindArg2 { + static const T& find(const A& a, const B&...) noexcept + { + return a; + } +}; + +template struct SubstituterBase::FindArg1 { + static const T& find(const A& a, const B&... b) noexcept + { + using P = typename std::remove_reference::type*; + return FindArg2::value, A, B...>::find(a, b...); + } +}; + +} // namespace _private + +template struct Substituter::Substitution { + size_type begin, end; + const typename Variables::value_type* var_def; +}; + +template inline Substituter::Substituter(SubstituterConfig config) noexcept : + m_lenient{config.lenient}, + m_logger{config.logger ? *config.logger : s_default_logger} +{ +} + +template inline auto Substituter::operator[](const char* name) noexcept -> ProtoDef +{ + return ProtoDef{*this, name}; +} + +template +inline bool Substituter::expand(StringView text, std::ostream& out, A&&... arg) const +{ + Template templ; + if (parse(text, templ)) { // Throws + templ.expand(out, std::forward(arg)...); // Throws + return true; + } + return false; +} + +template inline bool Substituter::parse(StringView text, Template& templ) const +{ + return parse(text, templ, m_logger); // Throws +} + +template +bool Substituter::parse(StringView text, Template& templ, Logger& logger) const +{ + bool error = false; + Logger::Level log_level = (m_lenient ? Logger::Level::warn : Logger::Level::error); + std::vector substitutions; + StringView var_name; + size_type curr = 0; + size_type end = text.size(); + for (;;) { + size_type i = text.find('@', curr); + if (i == StringView::npos) + break; + if (i + 1 == end) { + logger.log(log_level, "Unterminated `@` at end of text"); // Throws + error = true; + break; + } + char ch = text[i + 1]; + if (ch == '{') { + size_type j = text.find('}', i + 2); + if (j == StringView::npos) { + logger.log(log_level, "Unterminated `@{`"); // Throws + error = true; + curr = i + 2; + continue; + } + var_name = text.substr(i + 2, j - (i + 2)); + curr = j + 1; + } + else { + var_name = text.substr(i + 1, 1); // Throws + curr = i + 2; + } + const typename Variables::value_type* var_def = nullptr; + if (ch != '@') { + auto k = m_variables.find(var_name); + if (k == m_variables.end()) { + logger.log(log_level, "Undefined variable `%1` in substitution `%2`", var_name, + text.substr(i, curr - i)); // Throws + error = true; + continue; + } + var_def = &*k; + } + substitutions.push_back({i, curr, var_def}); // Throws + } + if (error && !m_lenient) + return false; + templ.m_text = text; + templ.m_substitutions = std::move(substitutions); + return true; +} + +template +inline void Substituter::define(const char* name, std::function func) +{ + auto p = m_variables.emplace(name, std::move(func)); // Throws + bool was_inserted = p.second; + if (!was_inserted) + throw std::runtime_error("Multiple definitions for same variable name"); +} + +template template inline void Substituter::ProtoDef::operator=(T* var) +{ + *this = [var](std::ostream& out, const A&...) { + out << *var; // Throws + }; +} + +template +template inline void Substituter::ProtoDef::operator=(T C::* var) +{ + *this = [var](std::ostream& out, const A&... arg) { + const C& obj = FindArg1::find(arg...); + out << obj.*var; // Throws + }; +} + +template +inline void Substituter::ProtoDef::operator=(std::function func) +{ + m_substituter.define(m_name, std::move(func)); // Throws +} + +template +inline Substituter::ProtoDef::ProtoDef(Substituter& substituter, const char* name) noexcept : + m_substituter{substituter}, + m_name{name} +{ +} + +template std::string Substituter::Template::expand(A&&... arg) const +{ + std::ostringstream out; + out.imbue(std::locale::classic()); + expand(out, std::forward(arg)...); // Throws + std::string str = std::move(out).str(); // Throws + return str; +} + +template void Substituter::Template::expand(std::ostream& out, A... arg) const +{ + std::ios_base::fmtflags flags = out.flags(); + try { + size_type curr = 0; + for (const Substitution& subst: m_substitutions) { + out << m_text.substr(curr, subst.begin - curr); // Throws + if (subst.var_def) { + const std::function& eval_func = subst.var_def->second; + eval_func(out, arg...); // Throws + out.flags(flags); + } + else { + out << "@"; // Throws + } + curr = subst.end; + } + out << m_text.substr(curr); // Throws + } + catch (...) { + out.flags(flags); + throw; + } +} + +template +inline bool Substituter::Template::refers_to(const char* name) const noexcept +{ + StringView name_2 = name; + for (const auto& subst: m_substitutions) { + if (subst.var_def) { + if (name_2 != subst.var_def->first) + continue; + return true; + } + } + return false; +} + +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_SUBSTITUTE_HPP diff --git a/src/vendor-include/realm-ios/include/realm/util/system_process.hpp b/src/vendor-include/realm-ios/include/realm/util/system_process.hpp new file mode 100644 index 000000000..c84adddde --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/system_process.hpp @@ -0,0 +1,204 @@ +/************************************************************************* + * + * 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_SYSTEM_PROCESS_HPP +#define REALM_UTIL_SYSTEM_PROCESS_HPP + +#include +#include +#include +#include + +#include + + +namespace realm { +namespace util { +namespace sys_proc { + +using Environment = std::map; + +/// This function is safe to call only when the caller can be sure that there +/// are no threads that modify the environment concurrently. +/// +/// When possible, call this function from the main thread before any other +/// threads are created, such as early in `main()`. +Environment copy_local_environment(); + + +struct ExitInfo { + /// If nonzero, the process was killed by a signal. The value is the + /// signal number. + int killed_by_signal = 0; + + /// Zero if the process was killed by a signal, otherwise this is the value + /// returned by the `main()` function, or passed to `exit()`. + /// + /// On a POSIX system, if an error occurs during ::execve(), that is, after + /// ::fork(), an exit status of 127 will be used (aligned with + /// ::posix_spawn()). + int status = 0; + + /// In some cases, ChildHandle::join() will set `signal_name` when it sets + /// `killed_by_signal` to a non-zero value. In those cases, `signal_name` is + /// set to point to a null-terminated string specifying the name of the + /// signal that killed the child process. + const char* signal_name = nullptr; + + /// Returns true if, and only if both `killed_by_signal` and `status` are + /// zero. + explicit operator bool() const noexcept; +}; + + +struct SpawnConfig { + /// When set to true, the child process will be able to use a + /// ParentDeathGuard to detect the destruction of the SystemProcess object + /// in the parent process, even when this happens implicitly due to abrupt + /// termination of the parent process. + bool parent_death_guard = false; + + /// If a logger is specified here, the child process will be able to + /// instantiate a ParentLogger object, and messages logged through that + /// ParentLogger object will be transported to the parent process and + /// submitted to the logger pointed to by `logger`. The specified logger is + /// guaranteed to only be accessed while ChildHandle::join() is executing, + /// and only by the thread that executes ChildHandle::join(). See + /// ParentLogger for further details. + Logger* logger = nullptr; +}; + + +class ChildHandle { +public: + /// Wait for the child process to exit. + /// + /// If a logger was passed to spawn() (SpawnConfig::logger), then this + /// function will also transport log messages from the child to the parent + /// process while waiting for the child process to exit. See ParentLogger + /// for details. + ExitInfo join(); + + ChildHandle(ChildHandle&&) noexcept; + ~ChildHandle() noexcept; + +private: + class Impl; + std::unique_ptr m_impl; + + ChildHandle(Impl*) noexcept; + + friend ChildHandle spawn(const std::string&, const std::vector&, + const Environment&, const SpawnConfig&); +}; + + +/// Returns true if, and only if the spawn() functions work on this platform. If +/// this function returns false, the spawn() functions will throw. +bool is_spawn_supported() noexcept; + + +//@{ +/// Spawn a child process. +ChildHandle spawn(const std::string& path, const std::vector& args = {}, + const Environment& = {}); +ChildHandle spawn(const std::string& path, const std::vector& args, + const Environment&, const SpawnConfig&); +//@} + + +/// Force a child process to terminate immediately if the parent process is +/// terminated, or if the parent process destroys the ChildHandle object +/// representing the child process. +/// +/// If a child process instantiates an object of this type, and keeps it alive, +/// and the child process was spawned with support for detection of parent +/// termination (SpawnConfig::parent_death_guard), then the child process will +/// be killed shortly after the parent destroys its ChildHandle object, even +/// when this happens implicitly due to abrupt termination of the parent +/// process. +/// +/// If a child process instantiates an object of this type, that object must be +/// instantiated by the main thread, and before any other thread is spawned in +/// the child process. +/// +/// In order for the guard to have the intended effect, it must be instantiated +/// immediately in the child process, and be kept alive for as long as the child +/// process is running. +class ParentDeathGuard { +public: + ParentDeathGuard(); + ~ParentDeathGuard() noexcept; + +private: + std::thread m_thread; + int m_stop_pipe_write = -1; +}; + + +/// A logger that can transport log messages from the child to the parent +/// process. +/// +/// If the parent process specifies a logger when spawning a child process +/// (SpawnConfig::logger), then that child process can instantiate a +/// ParentLogger object, and messages logged through it will be transported to +/// the parent process. While the parent process is executing +/// ChildHandle::join(), those messages will be written to the logger specified +/// by the parent process. +/// +/// If a child process instantiates an object of this type, that object must be +/// instantiated by the main thread, and before any other thread is spawned in +/// the child process. +/// +/// At most one ParentLogger object may be instantiated per child process. +/// +/// This logger is **not** thread-safe. +class ParentLogger : public RootLogger { +public: + ParentLogger(); + ~ParentLogger() noexcept; + +protected: + void do_log(Level, std::string) override final; + +private: + int m_pipe_write = -1; +}; + + + + +// Implementation + +inline ExitInfo::operator bool() const noexcept +{ + return (killed_by_signal == 0 && status == 0); +} + +inline ChildHandle spawn(const std::string& path, const std::vector& args, + const Environment& env) +{ + return spawn(path, args, env, SpawnConfig{}); // Throws +} + +} // namespace sys_proc +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_SYSTEM_PROCESS_HPP diff --git a/src/vendor-include/realm-ios/include/realm/util/terminate.hpp b/src/vendor-include/realm-ios/include/realm/util/terminate.hpp new file mode 100644 index 000000000..4e6034e8f --- /dev/null +++ b/src/vendor-include/realm-ios/include/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/src/vendor-include/realm-ios/include/realm/util/thread.hpp b/src/vendor-include/realm-ios/include/realm/util/thread.hpp new file mode 100644 index 000000000..9499086bf --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/thread.hpp @@ -0,0 +1,814 @@ +/************************************************************************* + * + * 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 +#include // for windows non-interprocess condvars we use std::condition_variable +#include +#include // _getpid() +#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&&) noexcept; + + /// 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) noexcept; + +private: + +#ifdef _WIN32 + std::thread m_std_thread; +#else + pthread_t m_id; +#endif + 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; + friend class InterprocessCondVar; + + void lock() noexcept; + bool try_lock() noexcept; + void unlock() noexcept; + +protected: +#ifdef _WIN32 + // Used for non-process-shared mutex. We only know at runtime whether or not to use it, depending on if we call + // Mutex::Mutex(process_shared_tag) + CRITICAL_SECTION m_critical_section; +#else + pthread_mutex_t m_impl = PTHREAD_MUTEX_INITIALIZER; +#endif + + 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; + +private: + friend class CondVar; + friend class RobustMutex; +}; + + +/// 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: +#ifdef _WIN32 + CONDITION_VARIABLE m_condvar = CONDITION_VARIABLE_INIT; +#else + pthread_cond_t m_impl; +#endif + + 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); +}; + + +class RaceDetector { + std::atomic busy; + +public: + RaceDetector() + { + busy.store(false); + } + void enter() + { + bool already_busy = busy.exchange(true, std::memory_order_acq_rel); + if (already_busy) + throw std::runtime_error("Race detected - critical section busy on entry"); + } + void leave() + { + busy.store(false, std::memory_order_release); + } + friend class CriticalSection; +}; + +class CriticalSection { + RaceDetector& rd; + +public: + CriticalSection(RaceDetector& race) + : rd(race) + { + rd.enter(); + } + ~CriticalSection() + { + rd.leave(); + } +}; + +// 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) noexcept +{ +#ifndef _WIN32 + m_id = thread.m_id; + m_joinable = thread.m_joinable; + thread.m_joinable = false; +#endif +} + +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) +{ +#ifdef _WIN32 + m_std_thread = std::thread(entry_func, arg); +#else + 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 +#endif +} + +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 +{ +#ifndef _WIN32 + int r = pthread_mutex_destroy(&m_impl); + if (REALM_UNLIKELY(r != 0)) + destroy_failed(r); +#else + DeleteCriticalSection(&m_critical_section); +#endif +} + +inline void Mutex::init_as_regular() +{ +#ifndef _WIN32 + int r = pthread_mutex_init(&m_impl, 0); + if (REALM_UNLIKELY(r != 0)) + init_failed(r); +#else + InitializeCriticalSection(&m_critical_section); +#endif +} + +inline void Mutex::lock() noexcept +{ +#ifdef _WIN32 + EnterCriticalSection(&m_critical_section); +#else + int r = pthread_mutex_lock(&m_impl); + if (REALM_LIKELY(r == 0)) + return; + lock_failed(r); +#endif +} + +inline bool Mutex::try_lock() noexcept +{ +#ifdef _WIN32 + return TryEnterCriticalSection(&m_critical_section); +#else + int r = pthread_mutex_trylock(&m_impl); + if (r == EBUSY) { + return false; + } + else if (r == 0) { + return true; + } + lock_failed(r); +#endif +} + +inline void Mutex::unlock() noexcept +{ +#ifdef _WIN32 + LeaveCriticalSection(&m_critical_section); +#else + int r = pthread_mutex_unlock(&m_impl); + REALM_ASSERT(r == 0); +#endif +} + + +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() +{ +#ifndef _WIN32 + int r = pthread_cond_init(&m_impl, 0); + if (REALM_UNLIKELY(r != 0)) + init_failed(r); +#endif +} + +inline CondVar::~CondVar() noexcept +{ +#ifndef _WIN32 + int r = pthread_cond_destroy(&m_impl); + if (REALM_UNLIKELY(r != 0)) + destroy_failed(r); +#endif +} + +inline void CondVar::wait(LockGuard& l) noexcept +{ +#ifdef _WIN32 + SleepConditionVariableCS(&m_condvar, &l.m_mutex.m_critical_section, INFINITE); +#else + int r = pthread_cond_wait(&m_impl, &l.m_mutex.m_impl); + if (REALM_UNLIKELY(r != 0)) + REALM_TERMINATE("pthread_cond_wait() failed"); +#endif +} + +template +inline void CondVar::wait(RobustMutex& m, Func recover_func, const struct timespec* tp) +{ + int r; + + if (!tp) { +#ifdef _WIN32 + if (!SleepConditionVariableCS(&m_condvar, &m.m_critical_section, INFINITE)) + r = GetLastError(); + else + r = 0; +#else + r = pthread_cond_wait(&m_impl, &m.m_impl); +#endif + } + else { +#ifdef _WIN32 + if (!SleepConditionVariableCS(&m_condvar, &m.m_critical_section, tp->tv_sec / 1000)) { + r = GetLastError(); + if (r == ERROR_TIMEOUT) + return; + } else { + r = 0 + } +#else + r = pthread_cond_timedwait(&m_impl, &m.m_impl, tp); + if (r == ETIMEDOUT) + return; +#endif + } + + 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 +{ +#ifdef _WIN32 + WakeConditionVariable(&m_condvar); +#else + int r = pthread_cond_signal(&m_impl); + REALM_ASSERT(r == 0); +#endif +} + +inline void CondVar::notify_all() noexcept +{ +#ifdef _WIN32 + WakeAllConditionVariable(&m_condvar); +#else + int r = pthread_cond_broadcast(&m_impl); + REALM_ASSERT(r == 0); +#endif +} + +// helpers which can ensure atomic access to memory which has not itself been declared atomic. +// This can be used to e.g. ensure atomic access to members of a vector. Vectors does not +// fully allow atomic members because operations on vector may relocate the underlying memory. +// use with care! +template +T load_atomic(T& t_ref, std::memory_order order) +{ + std::atomic* t_ptr = reinterpret_cast*>(&t_ref); + T t = atomic_load_explicit(t_ptr, order); + return t; +} + +template +void store_atomic(T& t_ref, T value, std::memory_order order) +{ + std::atomic* t_ptr = reinterpret_cast*>(&t_ref); + atomic_store_explicit(t_ptr, value, order); +} + + +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_THREAD_HPP diff --git a/src/vendor-include/realm-ios/include/realm/util/thread_exec_guard.hpp b/src/vendor-include/realm-ios/include/realm/util/thread_exec_guard.hpp new file mode 100644 index 000000000..b779e5a5e --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/thread_exec_guard.hpp @@ -0,0 +1,334 @@ +/************************************************************************* + * + * 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_THREAD_EXEC_GUARD_HPP +#define REALM_UTIL_THREAD_EXEC_GUARD_HPP + +#include +#include +#include + +#include +#include + + +namespace realm { +namespace util { + +/// Execute a `R::run()` using a managed thread. +/// +/// \tparam R The type of the runnable object. This type must satisfy the +/// requirements of the Runnable concept. See ThreadExecGuardWithParent. +template class ThreadExecGuard { +public: + explicit ThreadExecGuard(R& runnable); + + ThreadExecGuard(ThreadExecGuard&&) = default; + + /// If start() or start_with_signals_blocked() was successfully executed, + /// and stop_and_rethrow() has not been called, call `R::stop()`, and then + /// wait for the thread to terminate (join). + ~ThreadExecGuard() noexcept = default; + + // @{ + /// Launch a thread and make it execute `R::run()` of the associated + /// "runnable" object. + /// + /// At most one of these functions are allowed to be called on a particular + /// guard object, and it must only be called once. + void start(); + void start(const std::string& thread_name); + void start_with_signals_blocked(); + void start_with_signals_blocked(const std::string& thread_name); + // @} + + /// If start() or start_with_signals_blocked() was successfully executed, + /// call `R::stop()`, wait for the thread to terminate (join), and then, if + /// an exception was thrown by `R::run()`, rethrow it. + void stop_and_rethrow(); + +private: + struct State { + R& runnable; + util::Thread thread; + std::exception_ptr exception; + State(R&) noexcept; + ~State() noexcept; + void start(const std::string* thread_name); + void stop_and_rethrow(); + }; + + std::unique_ptr m_state; +}; + + +/// Execute a `R::run()` using a managed thread. +/// +/// \tparam R The type of the runnable object. This type must satisfy the +/// requirements of the Runnable concept. See below. +/// +/// \tparam P The type of the object representing the parent thread. This type +/// must satisfy the requirements of the Stoppable concept. See below. +/// +/// A type satisfies the requirements of the *Stoppable* concept, if +/// - it has a nonthrowing member function named `stop()`, and +/// - `stop()` is thread-safe, and +/// - `stop()` is idempotent (can be called multiple times). +/// +/// A type satisfies the requirements of the *Runnable* concept, if +/// - it satisfies the requirements of the Stoppable concept, and +/// - it has a member function named `run()`, and +/// - `run()` will stop executing within a reasonable amount of time after +/// `stop()` has been called. +/// +template class ThreadExecGuardWithParent { +public: + explicit ThreadExecGuardWithParent(R& runnable, P& parent); + + ThreadExecGuardWithParent(ThreadExecGuardWithParent&&) = default; + + /// If start() or start_with_signals_blocked() was successfully executed, + /// and stop_and_rethrow() has not been called, call `R::stop()`, and then + /// wait for the thread to terminate (join). + ~ThreadExecGuardWithParent() noexcept = default; + + // @{ + /// Launch a thread and make it execute `R::run()` of the associated + /// "runnable" object. + /// + /// If `R::run()` throws, call `P::stop()` on the specified parent. + /// + /// At most one of these functions are allowed to be called on a particular + /// guard object, and it must only be called once. + void start(); + void start(const std::string& thread_name); + void start_with_signals_blocked(); + void start_with_signals_blocked(const std::string& thread_name); + // @} + + /// If start() or start_with_signals_blocked() was successfully executed, + /// call `R::stop()`, wait for the thread to terminate (join), and then, if + /// an exception was thrown by `R::run()`, rethrow it. + void stop_and_rethrow(); + +private: + struct State { + R& runnable; + P& parent; + util::Thread thread; + std::exception_ptr exception; + State(R&, P&) noexcept; + ~State() noexcept; + void start(const std::string* thread_name); + void stop_and_rethrow(); + }; + + std::unique_ptr m_state; +}; + + +template ThreadExecGuard make_thread_exec_guard(R& runnable); + +template +ThreadExecGuardWithParent make_thread_exec_guard(R& runnable, P& parent); + + + + +// Implementation + +template inline ThreadExecGuard::ThreadExecGuard(R& runnable) : + m_state{std::make_unique(runnable)} // Throws +{ +} + +template inline void ThreadExecGuard::start() +{ + const std::string* thread_name = nullptr; + m_state->start(thread_name); // Throws +} + +template inline void ThreadExecGuard::start(const std::string& thread_name) +{ + m_state->start(&thread_name); // Throws +} + +template inline void ThreadExecGuard::start_with_signals_blocked() +{ + SignalBlocker sb; + const std::string* thread_name = nullptr; + m_state->start(thread_name); // Throws +} + +template +inline void ThreadExecGuard::start_with_signals_blocked(const std::string& thread_name) +{ + SignalBlocker sb; + m_state->start(&thread_name); // Throws +} + +template inline void ThreadExecGuard::stop_and_rethrow() +{ + m_state->stop_and_rethrow(); // Throws +} + +template inline ThreadExecGuard::State::State(R& r) noexcept : + runnable{r} +{ +} + +template inline ThreadExecGuard::State::~State() noexcept +{ + if (thread.joinable()) { + runnable.stop(); + thread.join(); + } +} + +template inline void ThreadExecGuard::State::start(const std::string* thread_name) +{ + bool set_thread_name = false; + std::string thread_name_2; + if (thread_name) { + set_thread_name = true; + thread_name_2 = *thread_name; // Throws (copy) + } + auto run = [this, set_thread_name, thread_name=std::move(thread_name_2)]() noexcept { + try { + if (set_thread_name) + util::Thread::set_name(thread_name); // Throws + runnable.run(); // Throws + } + catch (...) { + exception = std::current_exception(); + } + }; + thread.start(std::move(run)); // Throws +} + +template inline void ThreadExecGuard::State::stop_and_rethrow() +{ + if (thread.joinable()) { + runnable.stop(); + thread.join(); + if (exception) + std::rethrow_exception(exception); // Throws + } +} + +template +inline ThreadExecGuardWithParent::ThreadExecGuardWithParent(R& runnable, P& parent) : + m_state{std::make_unique(runnable, parent)} // Throws +{ +} + +template inline void ThreadExecGuardWithParent::start() +{ + const std::string* thread_name = nullptr; + m_state->start(thread_name); // Throws +} + +template +inline void ThreadExecGuardWithParent::start(const std::string& thread_name) +{ + m_state->start(&thread_name); // Throws +} + +template inline void ThreadExecGuardWithParent::start_with_signals_blocked() +{ + SignalBlocker sb; + const std::string* thread_name = nullptr; + m_state->start(thread_name); // Throws +} + +template +inline void ThreadExecGuardWithParent::start_with_signals_blocked(const std::string& thread_name) +{ + SignalBlocker sb; + m_state->start(&thread_name); // Throws +} + +template inline void ThreadExecGuardWithParent::stop_and_rethrow() +{ + m_state->stop_and_rethrow(); // Throws +} + +template +inline ThreadExecGuardWithParent::State::State(R& r, P& p) noexcept : + runnable{r}, + parent{p} +{ +} + +template inline ThreadExecGuardWithParent::State::~State() noexcept +{ + if (thread.joinable()) { + runnable.stop(); + thread.join(); + } +} + +template +inline void ThreadExecGuardWithParent::State::start(const std::string* thread_name) +{ + bool set_thread_name = false; + std::string thread_name_2; + if (thread_name) { + set_thread_name = true; + thread_name_2 = *thread_name; // Throws (copy) + } + auto run = [this, set_thread_name, thread_name=std::move(thread_name_2)]() noexcept { + try { + if (set_thread_name) + util::Thread::set_name(thread_name); // Throws + runnable.run(); // Throws + } + catch (...) { + exception = std::current_exception(); + parent.stop(); + } + }; + thread.start(std::move(run)); // Throws +} + +template inline void ThreadExecGuardWithParent::State::stop_and_rethrow() +{ + if (thread.joinable()) { + runnable.stop(); + thread.join(); + if (exception) + std::rethrow_exception(exception); // Throws + } +} + +template inline ThreadExecGuard make_thread_exec_guard(R& runnable) +{ + return ThreadExecGuard{runnable}; // Throws +} + +template +inline ThreadExecGuardWithParent make_thread_exec_guard(R& runnable, P& parent) +{ + return ThreadExecGuardWithParent{runnable, parent}; // Throws +} + +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_THREAD_EXEC_GUARD_HPP diff --git a/src/vendor-include/realm-ios/include/realm/util/time.hpp b/src/vendor-include/realm-ios/include/realm/util/time.hpp new file mode 100644 index 000000000..75d986132 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/time.hpp @@ -0,0 +1,94 @@ +/************************************************************************* + * + * 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_TIME_HPP +#define REALM_UTIL_TIME_HPP + +#include +#include +#include +#include +#include +#include + + +namespace realm { +namespace util { + +/// Thread safe version of std::localtime(). Uses localtime_r() on POSIX. +std::tm localtime(std::time_t); + +/// Thread safe version of std::gmtime(). Uses gmtime_r() on POSIX. +std::tm gmtime(std::time_t); + +/// Similar to std::put_time() from . See std::put_time() for +/// information about the format string. This function is provided because +/// std::put_time() is unavailable in GCC 4. This function is thread safe. +/// +/// The default format is ISO 8601 date and time. +template +void put_time(std::basic_ostream&, const std::tm&, const C* format = "%FT%T%z"); + +// @{ +/// These functions combine localtime() or gmtime() with put_time() and +/// std::ostringstream. For detals on the format string, see +/// std::put_time(). These function are thread safe. +std::string format_local_time(std::time_t, const char* format = "%FT%T%z"); +std::string format_utc_time(std::time_t, const char* format = "%FT%T%z"); +// @} + +/// The local time since the epoch in microseconds. +/// +/// FIXME: This function has nothing to do with local time. +double local_time_microseconds(); + + + + +// Implementation + +template +inline void put_time(std::basic_ostream& out, const std::tm& tm, const C* format) +{ + const auto& facet = std::use_facet>(out.getloc()); // Throws + facet.put(std::ostreambuf_iterator(out), out, ' ', &tm, + format, format + T::length(format)); // Throws +} + +inline std::string format_local_time(std::time_t time, const char* format) +{ + std::tm tm = util::localtime(time); + std::ostringstream out; + util::put_time(out, tm, format); // Throws + return out.str(); // Throws +} + +inline std::string format_utc_time(std::time_t time, const char* format) +{ + std::tm tm = util::gmtime(time); + std::ostringstream out; + util::put_time(out, tm, format); // Throws + return out.str(); // Throws +} + +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_TIME_HPP diff --git a/src/vendor-include/realm-ios/include/realm/util/timestamp_formatter.hpp b/src/vendor-include/realm-ios/include/realm/util/timestamp_formatter.hpp new file mode 100644 index 000000000..47e4ce3f8 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/timestamp_formatter.hpp @@ -0,0 +1,110 @@ +/************************************************************************* + * + * 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_TIMESTAMP_FORMATTER_HPP +#define REALM_UTIL_TIMESTAMP_FORMATTER_HPP + +#include +#include +#include +#include + +#include +#include +#include +#include + + +namespace realm { +namespace util { + +class TimestampFormatter { +public: + using char_type = char; + using string_view_type = util::BasicStringView; + + enum class Precision { seconds, milliseconds, microseconds, nanoseconds }; + + /// Default configuration for corresponds to local time in ISO 8601 date and + /// time format. + struct Config { + Config() {} + + bool utc_time = false; + + Precision precision = Precision::seconds; + + /// The format of the timestamp as understood by std::put_time(), except + /// that the first occurrence of `%S` (also taking into account the `%S` + /// that is an implicit part of `%T`) is expanded to `SS.fff` if \ref + /// precision is Precision::milliseconds, or to `SS.ffffff` if \ref + /// precision is Precision::microseconds, or to `SS.fffffffff` if \ref + /// precision is Precision::nanoseconds, where `SS` is what `%S` expands + /// to conventionally. + const char* format = "%FT%T%z"; + }; + + TimestampFormatter(Config = {}); + + // FIXME: Use std::timespec in C++17. + string_view_type format(std::time_t time, long nanoseconds); + + template string_view_type format(std::chrono::time_point); + +private: + using memory_output_stream_type = util::MemoryOutputStream; + using format_segments_type = std::pair; + + const bool m_utc_time; + const Precision m_precision; + const format_segments_type m_format_segments; + char_type m_buffer[64]; + memory_output_stream_type m_out; + + static format_segments_type make_format_segments(const Config&); +}; + + + + + +// Implementation + +template +inline auto TimestampFormatter::format(std::chrono::time_point time) -> string_view_type +{ + using clock_type = B; + using time_point_type = std::chrono::time_point; + std::time_t time_2 = clock_type::to_time_t(time); + time_point_type time_3 = clock_type::from_time_t(time_2); + if (REALM_UNLIKELY(time_3 > time)) { + --time_2; + time_3 = clock_type::from_time_t(time_2); + } + long nanoseconds = + int(std::chrono::duration_cast(time - time_3).count()); + REALM_ASSERT(nanoseconds >= 0 && nanoseconds < 1000000000); + return format(time_2, nanoseconds); // Throws +} + +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_TIMESTAMP_FORMATTER_HPP diff --git a/src/vendor-include/realm-ios/include/realm/util/timestamp_logger.hpp b/src/vendor-include/realm-ios/include/realm/util/timestamp_logger.hpp new file mode 100644 index 000000000..a9789a5a4 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/timestamp_logger.hpp @@ -0,0 +1,49 @@ +/************************************************************************* + * + * 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_TIMESTAMP_LOGGER_HPP +#define REALM_UTIL_TIMESTAMP_LOGGER_HPP + +#include +#include + + +namespace realm { +namespace util { + +class TimestampStderrLogger : public RootLogger { +public: + using Precision = TimestampFormatter::Precision; + using Config = TimestampFormatter::Config; + + explicit TimestampStderrLogger(Config = {}); + +protected: + void do_log(Logger::Level, std::string message) override; + +private: + TimestampFormatter m_formatter; +}; + + +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_TIMESTAMP_LOGGER_HPP diff --git a/src/vendor-include/realm-ios/include/realm/util/to_string.hpp b/src/vendor-include/realm-ios/include/realm/util/to_string.hpp new file mode 100644 index 000000000..700786ce8 --- /dev/null +++ b/src/vendor-include/realm-ios/include/realm/util/to_string.hpp @@ -0,0 +1,127 @@ +/************************************************************************* + * + * 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) + { + } + Printable(std::string const& value) + : m_type(Type::String) + , m_string(value.c_str()) + { + } + + + 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(); +} + + +std::string format(const char* fmt, std::initializer_list); + +template +std::string format(const char* fmt, Args&&... args) +{ + return format(fmt, {Printable(args)...}); +} + + +} // namespace util +} // namespace realm + +#endif // REALM_UTIL_TO_STRING_HPP diff --git a/src/vendor-include/realm-ios/include/realm/util/type_list.hpp b/src/vendor-include/realm-ios/include/realm/util/type_list.hpp new file mode 100644 index 000000000..da847c751 --- /dev/null +++ b/src/vendor-include/realm-ios/include/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